From 198733a763d927bcf4fc7ae2ba50489d4de7a772 Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 1 Jun 2020 22:00:49 +0200 Subject: [PATCH 01/89] started implementing an abstract item model for channels --- .../src/gui/gxschannels/GxsChannelDialog.cpp | 4 +- .../gui/gxschannels/GxsChannelFilesWidget.cpp | 11 +- .../gui/gxschannels/GxsChannelPostsWidget.cpp | 4 - .../gui/gxschannels/GxsChannelPostsWidget.h | 2 +- .../GxsChannelPostsWidgetWithModel.cpp | 953 ++++++++++++++++++ .../GxsChannelPostsWidgetWithModel.h | 130 +++ .../GxsChannelPostsWidgetWithModel.ui | 563 +++++++++++ retroshare-gui/src/retroshare-gui.pro | 8 +- 8 files changed, 1666 insertions(+), 9 deletions(-) create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp index 8e25e4ea0..23e0854a6 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelDialog.cpp @@ -26,7 +26,7 @@ #include "GxsChannelDialog.h" #include "GxsChannelGroupDialog.h" -#include "GxsChannelPostsWidget.h" +#include "GxsChannelPostsWidgetWithModel.h" #include "CreateGxsChannelMsg.h" #include "GxsChannelUserNotify.h" #include "gui/gxs/GxsGroupShareKey.h" @@ -204,7 +204,7 @@ int GxsChannelDialog::shareKeyType() GxsMessageFrameWidget *GxsChannelDialog::createMessageFrameWidget(const RsGxsGroupId &groupId) { - return new GxsChannelPostsWidget(groupId); + return new GxsChannelPostsWidgetWithModel(groupId,this); } void GxsChannelDialog::setDefaultDirectory() diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp index 3aa6ccae9..3afb4e645 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp @@ -80,13 +80,22 @@ GxsChannelFilesWidget::GxsChannelFilesWidget(QWidget *parent) : ui->treeWidget->setColumnWidth(COLUMN_PUBLISHED, 150); } +GxsChannelFilesWidget::paintEvent() +{ + QWidget::paintEvent(); + + if(!mLoaded) + { + } +} + GxsChannelFilesWidget::~GxsChannelFilesWidget() { delete(mCompareRole); delete ui; } -void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost &post, bool related) +void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost& post, bool related) { if (related) { removeItems(post.mMeta.mGroupId, post.mMeta.mMsgId); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp index d4e6192e2..5d6974442 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp @@ -551,13 +551,9 @@ void GxsChannelPostsWidget::createPostItem(const RsGxsChannelPost& post, bool re ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs)); } -#ifdef TODO ui->fileWidget->addFiles(post, related); -#endif } - - void GxsChannelPostsWidget::fillThreadCreatePost(const QVariant &post, bool related, int current, int count) { /* show fill progress */ diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h index 025d933bf..28a521944 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h @@ -98,7 +98,7 @@ private: int viewMode(); void insertChannelDetails(const RsGxsChannelGroup &group); - void insertChannelPosts(std::vector &posts, GxsMessageFramePostThread *thread, bool related); + void insertChannelPosts(std::vector &posts); void createPostItem(const RsGxsChannelPost &post, bool related); void handleEvent_main_thread(std::shared_ptr event); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp new file mode 100644 index 000000000..bc3c564e3 --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -0,0 +1,953 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.cpp * + * * + * Copyright 2013 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include + +#include "retroshare/rsgxscircles.h" + +#include "GxsChannelPostsWidgetWithModel.h" +#include "ui_GxsChannelPostsWidget.h" +#include "gui/feeds/GxsChannelPostItem.h" +#include "gui/gxs/GxsIdDetails.h" +#include "gui/gxschannels/CreateGxsChannelMsg.h" +#include "gui/common/UIStateHelper.h" +#include "gui/settings/rsharesettings.h" +#include "gui/feeds/SubFileItem.h" +#include "gui/notifyqt.h" +#include "gui/RetroShareLink.h" +#include "util/HandleRichText.h" +#include "util/DateTime.h" +#include "util/qtthreadsutils.h" + +#include + +#define CHAN_DEFAULT_IMAGE ":/icons/png/channels.png" + +#define ROLE_PUBLISH FEED_TREEWIDGET_SORTROLE + +/**** + * #define DEBUG_CHANNEL + ***/ + +/* View mode */ +#define VIEW_MODE_FEEDS 1 +#define VIEW_MODE_FILES 2 + +/** Constructor */ +GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : + GxsMessageFramePostWidget(rsGxsChannels, parent), + ui(new Ui::GxsChannelPostsWidget) +{ + /* Invoke the Qt Designer generated object setup routine */ + ui->setupUi(this); + + ui->postsTree->setModel(new RsGxsChannelPostsModel()); + + /* Setup UI helper */ + + mStateHelper->addWidget(mTokenTypeAllPosts, ui->progressBar, UISTATE_LOADING_VISIBLE); + mStateHelper->addWidget(mTokenTypeAllPosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); + mStateHelper->addWidget(mTokenTypeAllPosts, ui->filterLineEdit); + + mStateHelper->addWidget(mTokenTypePosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); + + mStateHelper->addLoadPlaceholder(mTokenTypeGroupData, ui->nameLabel); + + mStateHelper->addWidget(mTokenTypeGroupData, ui->postButton); + mStateHelper->addWidget(mTokenTypeGroupData, ui->logoLabel); + mStateHelper->addWidget(mTokenTypeGroupData, ui->subscribeToolButton); + + /* Connect signals */ + connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg())); + connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); + connect(NotifyQt::getInstance(), SIGNAL(settingsChanged()), this, SLOT(settingsChanged())); + + ui->postButton->setText(tr("Add new post")); + + /* add filter actions */ + ui->filterLineEdit->addFilter(QIcon(), tr("Title"), FILTER_TITLE, tr("Search Title")); + ui->filterLineEdit->addFilter(QIcon(), tr("Message"), FILTER_MSG, tr("Search Message")); + ui->filterLineEdit->addFilter(QIcon(), tr("Filename"), FILTER_FILE_NAME, tr("Search Filename")); +#ifdef TODO + connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->feedWidget, SLOT(setFilterText(QString))); + connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->fileWidget, SLOT(setFilterText(QString))); +#endif + connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged(int))); + + /* Initialize view button */ + //setViewMode(VIEW_MODE_FEEDS); see processSettings + ui->infoWidget->hide(); + + QSignalMapper *signalMapper = new QSignalMapper(this); + connect(ui->feedToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); + connect(ui->fileToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); + signalMapper->setMapping(ui->feedToolButton, VIEW_MODE_FEEDS); + signalMapper->setMapping(ui->fileToolButton, VIEW_MODE_FILES); + connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setViewMode(int))); + + /*************** Setup Left Hand Side (List of Channels) ****************/ + + ui->loadingLabel->hide(); + ui->progressBar->hide(); + + ui->nameLabel->setMinimumWidth(20); + + /* Initialize feed widget */ + ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); + ui->feedWidget->setFilterCallback(filterItem); + + /* load settings */ + processSettings(true); + + /* Initialize subscribe button */ + QIcon icon; + icon.addPixmap(QPixmap(":/images/redled.png"), QIcon::Normal, QIcon::On); + icon.addPixmap(QPixmap(":/images/start.png"), QIcon::Normal, QIcon::Off); + mAutoDownloadAction = new QAction(icon, "", this); + mAutoDownloadAction->setCheckable(true); + connect(mAutoDownloadAction, SIGNAL(triggered()), this, SLOT(toggleAutoDownload())); + + ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction); + + /* Initialize GUI */ + setAutoDownload(false); + settingsChanged(); + + mThreadModel->updateChannel(channelId); + + mEventHandlerId = 0; + // Needs to be asynced because this function is called by another thread! + rsEvents->registerEventsHandler( + [this](std::shared_ptr event) + { RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); }, + mEventHandlerId, RsEventType::GXS_CHANNELS ); +} + +void GxsChannelPostsWidget::handleEvent_main_thread(std::shared_ptr event) +{ + const RsGxsChannelEvent *e = dynamic_cast(event.get()); + + if(!e) + return; + + switch(e->mChannelEventCode) + { + case RsChannelEventCode::NEW_CHANNEL: // [[fallthrough]]; + case RsChannelEventCode::UPDATED_CHANNEL: // [[fallthrough]]; + case RsChannelEventCode::NEW_MESSAGE: // [[fallthrough]]; + case RsChannelEventCode::UPDATED_MESSAGE: + if(e->mChannelGroupId == groupId()) + updateDisplay(true); + break; + case RsChannelEventCode::READ_STATUS_CHANGED: + if (FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(e->mChannelMsgId))) + if (GxsChannelPostItem *channelPostItem = dynamic_cast(feedItem)) + channelPostItem->setReadStatus(false,!channelPostItem->isUnread()); + //channelPostItem->setReadStatus(false,e->Don't get read status. Will be more easier and accurate); + break; + default: + break; + } +} + +GxsChannelPostsWidget::~GxsChannelPostsWidget() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); + // save settings + processSettings(false); + + delete(mAutoDownloadAction); + + delete ui; +} + +void GxsChannelPostsWidget::processSettings(bool load) +{ + Settings->beginGroup(QString("ChannelPostsWidget")); + + if (load) { + // load settings + + /* Filter */ + ui->filterLineEdit->setCurrentFilter(Settings->value("filter", FILTER_TITLE).toInt()); + + /* View mode */ + setViewMode(Settings->value("viewMode", VIEW_MODE_FEEDS).toInt()); + } else { + // save settings + + /* Filter */ + Settings->setValue("filter", ui->filterLineEdit->currentFilter()); + + /* View mode */ + Settings->setValue("viewMode", viewMode()); + } + + Settings->endGroup(); +} + +void GxsChannelPostsWidget::settingsChanged() +{ + mUseThread = Settings->getChannelLoadThread(); + + mStateHelper->setWidgetVisible(ui->progressBar, mUseThread); +} + +void GxsChannelPostsWidget::groupNameChanged(const QString &name) +{ + if (groupId().isNull()) { + ui->nameLabel->setText(tr("No Channel Selected")); + ui->logoLabel->setPixmap(QPixmap(":/icons/png/channels.png")); + } else { + ui->nameLabel->setText(name); + } +} + +QIcon GxsChannelPostsWidget::groupIcon() +{ + if (mStateHelper->isLoading(mTokenTypeGroupData) || mStateHelper->isLoading(mTokenTypeAllPosts)) { + return QIcon(":/images/kalarm.png"); + } + +// if (mNewCount) { +// return QIcon(":/images/message-state-new.png"); +// } + + return QIcon(); +} + +/*************************************************************************************/ +/*************************************************************************************/ +/*************************************************************************************/ + +QScrollArea *GxsChannelPostsWidget::getScrollArea() +{ + return NULL; +} + +void GxsChannelPostsWidget::deleteFeedItem(FeedItem *feedItem, uint32_t /*type*/) +{ + if (!feedItem) + return; + + ui->feedWidget->removeFeedItem(feedItem); +} + +void GxsChannelPostsWidget::openChat(const RsPeerId & /*peerId*/) +{ +} + +// Callback from Widget->FeedHolder->ServiceDialog->CommentContainer->CommentDialog, +void GxsChannelPostsWidget::openComments(uint32_t /*type*/, const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title) +{ + emit loadComment(groupId, msg_versions,msgId, title); +} + +void GxsChannelPostsWidget::createMsg() +{ + if (groupId().isNull()) { + return; + } + + if (!IS_GROUP_SUBSCRIBED(subscribeFlags())) { + return; + } + + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(groupId()); + msgDialog->show(); + + /* window will destroy itself! */ +} + +void GxsChannelPostsWidget::insertChannelDetails(const RsGxsChannelGroup &group) +{ + /* IMAGE */ + QPixmap chanImage; + if (group.mImage.mData != NULL) { + GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); + } else { + chanImage = QPixmap(CHAN_DEFAULT_IMAGE); + } + ui->logoLabel->setPixmap(chanImage); + + if (group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH) + { + mStateHelper->setWidgetEnabled(ui->postButton, true); + } + else + { + mStateHelper->setWidgetEnabled(ui->postButton, false); + } + + ui->subscribeToolButton->setSubscribed(IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)); + mStateHelper->setWidgetEnabled(ui->subscribeToolButton, true); + + + bool autoDownload ; + rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); + setAutoDownload(autoDownload); + + RetroShareLink link; + + if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) { + ui->feedToolButton->setEnabled(true); + + ui->fileToolButton->setEnabled(true); + ui->infoWidget->hide(); + setViewMode(viewMode()); + + ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) ); + + + ui->infoPosts->clear(); + ui->infoDescription->clear(); + } else { + ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); + if(group.mMeta.mLastPost==0) + ui->infoLastPost->setText(tr("Never")); + else + ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); + QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); + + unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; + + // embed smileys ? + if (Settings->valueFromGroup(QString("ChannelPostsWidget"), QString::fromUtf8("Emoteicons_ChannelDecription"), true).toBool()) { + formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS; + } + + formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); + + ui->infoDescription->setText(formatDescription); + + ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; + + link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); + ui->infoAdministrator->setText(link.toHtml()); + + ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); + + QString distrib_string ( "[unknown]" ); + + switch(group.mMeta.mCircleType) + { + case GXS_CIRCLE_TYPE_PUBLIC: distrib_string = tr("Public") ; + break ; + case GXS_CIRCLE_TYPE_EXTERNAL: + { + RsGxsCircleDetails det ; + + // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. + + if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) + distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; + else + distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; + } + break ; + case GXS_CIRCLE_TYPE_YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); + break ; + case GXS_CIRCLE_TYPE_LOCAL: distrib_string = tr("You and your friend nodes"); + break ; + default: + std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; + } + + ui->infoDistribution->setText(distrib_string); + +#ifdef TODO + ui->infoWidget->show(); + ui->feedWidget->hide(); + ui->fileWidget->hide(); +#endif + + ui->feedToolButton->setEnabled(false); + ui->fileToolButton->setEnabled(false); + + ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); + + } +} + +int GxsChannelPostsWidget::viewMode() +{ + if (ui->feedToolButton->isChecked()) { + return VIEW_MODE_FEEDS; + } else if (ui->fileToolButton->isChecked()) { + return VIEW_MODE_FILES; + } + + /* Default */ + return VIEW_MODE_FEEDS; +} + +void GxsChannelPostsWidget::setViewMode(int viewMode) +{ +#ifdef TODO + switch (viewMode) { + case VIEW_MODE_FEEDS: + ui->feedWidget->show(); + ui->fileWidget->hide(); + + ui->feedToolButton->setChecked(true); + ui->fileToolButton->setChecked(false); + + break; + case VIEW_MODE_FILES: + ui->feedWidget->hide(); + ui->fileWidget->show(); + + ui->feedToolButton->setChecked(false); + ui->fileToolButton->setChecked(true); + + break; + default: + setViewMode(VIEW_MODE_FEEDS); + return; + } +#endif +} + +void GxsChannelPostsWidget::filterChanged(int filter) +{ +#ifdef TODO + ui->feedWidget->setFilterType(filter); + ui->fileWidget->setFilterType(filter); +#endif +} + +/*static*/ bool GxsChannelPostsWidget::filterItem(FeedItem *feedItem, const QString &text, int filter) +{ + GxsChannelPostItem *item = dynamic_cast(feedItem); + if (!item) { + return true; + } + + bool bVisible = text.isEmpty(); + + if (!bVisible) + { + switch(filter) + { + case FILTER_TITLE: + bVisible = item->getTitleLabel().contains(text,Qt::CaseInsensitive); + break; + case FILTER_MSG: + bVisible = item->getMsgLabel().contains(text,Qt::CaseInsensitive); + break; + case FILTER_FILE_NAME: + { + std::list fileItems = item->getFileItems(); + std::list::iterator lit; + for(lit = fileItems.begin(); lit != fileItems.end(); ++lit) + { + SubFileItem *fi = *lit; + QString fileName = QString::fromUtf8(fi->FileName().c_str()); + bVisible = (bVisible || fileName.contains(text,Qt::CaseInsensitive)); + } + break; + } + default: + bVisible = true; + break; + } + } + + return bVisible; +} + +#ifdef TODO +void GxsChannelPostsWidget::createPostItemFromMetaData(const RsGxsMsgMetaData& meta,bool related) +{ + GxsChannelPostItem *item = NULL; + RsGxsChannelPost post; + + if(!meta.mOrigMsgId.isNull()) + { + FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(meta.mOrigMsgId)) ; + item = dynamic_cast(feedItem); + + if(item) + { + post = feedItem->post(); + ui->feedWidget->removeFeedItem(item) ; + + post.mOlderVersions.insert(post.mMeta.mMsgId); + + GxsChannelPostItem *item = new GxsChannelPostItem(this, 0, post, true, false,post.mOlderVersions); + ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(post.mMeta.mPublishTs)); + + return ; + } + } + + if (related) + { + FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(meta.mMsgId)) ; + item = dynamic_cast(feedItem); + } + if (item) + { + item->setPost(post); + ui->feedWidget->setSort(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs)); + } + else + { + GxsChannelPostItem *item = new GxsChannelPostItem(this, 0, meta.mGroupId,meta.mMsgId, true, true); + ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(post.mMeta.mPublishTs)); + } +#ifdef TODO + ui->fileWidget->addFiles(post, related); +#endif +} +#endif + +void GxsChannelPostsWidget::insertChannelPosts(std::vector& posts) +{ + mModel->setPosts(posts); +} + +#ifdef TODO +void GxsChannelPostsWidget::createPostItem(const RsGxsChannelPost& post, bool related) +{ + GxsChannelPostItem *item = NULL; + + const RsMsgMetaData& meta(post.mMeta); + + if(!meta.mOrigMsgId.isNull()) + { + FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(meta.mOrigMsgId)) ; + item = dynamic_cast(feedItem); + + if(item) + { + std::set older_versions(item->olderVersions()); // we make a copy because the item will be deleted + ui->feedWidget->removeFeedItem(item) ; + + older_versions.insert(meta.mMsgId); + + GxsChannelPostItem *item = new GxsChannelPostItem(this, 0, mGroup.mMeta,meta.mMsgId, true, false,older_versions); + ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs)); + + return ; + } + } + + if (related) + { + FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(meta.mMsgId)) ; + item = dynamic_cast(feedItem); + } + if (item) + { + item->setPost(post); + ui->feedWidget->setSort(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs)); + } + else + { + GxsChannelPostItem *item = new GxsChannelPostItem(this, 0, mGroup.mMeta,meta.mMsgId, true, true); + ui->feedWidget->addFeedItem(item, ROLE_PUBLISH, QDateTime::fromTime_t(meta.mPublishTs)); + } + + ui->fileWidget->addFiles(post, related); +} + +void GxsChannelPostsWidget::fillThreadCreatePost(const QVariant &post, bool related, int current, int count) +{ + /* show fill progress */ + if (count) { + ui->progressBar->setValue(current * ui->progressBar->maximum() / count); + } + + if (!post.canConvert()) { + return; + } + + createPostItem(post.value(), related); +} + +void GxsChannelPostsWidget::insertChannelPosts(std::vector& posts, GxsMessageFramePostThread *thread, bool related) +{ + if (related && thread) { + std::cerr << "GxsChannelPostsWidget::insertChannelPosts fill only related posts as thread is not possible" << std::endl; + return; + } + + int count = posts.size(); + int pos = 0; + + if (!thread) { + ui->feedWidget->setSortingEnabled(false); + } + + // collect new versions of posts if any + +#ifdef DEBUG_CHANNEL + std::cerr << "Inserting channel posts" << std::endl; +#endif + + std::vector new_versions ; + for (uint32_t i=0;i search_map ; + for (uint32_t i=0;i versions ; + std::map::const_iterator vit ; + + while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId))) + { +#ifdef DEBUG_CHANNEL + std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ; +#endif + + // Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple + // times. In this case, we only need to replace the post with the newest version + + //uint32_t prev_index = current_index ; + current_index = vit->second ; + + if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further. + { +#ifdef DEBUG_CHANNEL + std::cerr << " already erased. Stopping." << std::endl; +#endif + break ; + } + + if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs) + { +#ifdef DEBUG_CHANNEL + std::cerr << " and is more recent => following" << std::endl; +#endif + for(std::set::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt) + posts[source_index].mOlderVersions.insert(*itt); + + posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId); + posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored + } +#ifdef DEBUG_CHANNEL + else + std::cerr << " but is older -> Stopping" << std::endl; +#endif + } + } + } + +#ifdef DEBUG_CHANNEL + std::cerr << "Now adding posts..." << std::endl; +#endif + + for (std::vector::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it) + { +#ifdef DEBUG_CHANNEL + std::cerr << " adding post: " << (*it).mMeta.mMsgId ; +#endif + + if(!(*it).mMeta.mMsgId.isNull()) + { +#ifdef DEBUG_CHANNEL + std::cerr << " added" << std::endl; +#endif + + if (thread && thread->stopped()) + break; + + if (thread) + thread->emitAddPost(QVariant::fromValue(*it), related, ++pos, count); + else + createPostItem(*it, related); + } +#ifdef DEBUG_CHANNEL + else + std::cerr << " skipped" << std::endl; +#endif + } + + if (!thread) { + ui->feedWidget->setSortingEnabled(true); + } +} + +void GxsChannelPostsWidget::clearPosts() +{ + ui->feedWidget->clear(); + ui->fileWidget->clear(); +} +#endif + +void GxsChannelPostsWidget::blank() +{ + mStateHelper->setWidgetEnabled(ui->postButton, false); + mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); + + clearPosts(); + + groupNameChanged(QString()); + + ui->infoWidget->hide(); + ui->feedWidget->show(); + ui->fileWidget->hide(); +} + +bool GxsChannelPostsWidget::navigatePostItem(const RsGxsMessageId &msgId) +{ + FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(msgId)); + if (!feedItem) { + return false; + } + + return ui->feedWidget->scrollTo(feedItem, true); +} + +void GxsChannelPostsWidget::subscribeGroup(bool subscribe) +{ + RsGxsGroupId grpId(groupId()); + if (grpId.isNull()) return; + + RsThread::async([=]() + { + rsGxsChannels->subscribeToChannel(grpId, subscribe); + } ); +} + +void GxsChannelPostsWidget::setAutoDownload(bool autoDl) +{ + mAutoDownloadAction->setChecked(autoDl); + mAutoDownloadAction->setText(autoDl ? tr("Disable Auto-Download") : tr("Enable Auto-Download")); +} + +void GxsChannelPostsWidget::toggleAutoDownload() +{ + RsGxsGroupId grpId = groupId(); + if (grpId.isNull()) { + return; + } + + bool autoDownload; + if(!rsGxsChannels->getChannelAutoDownload(grpId, autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value " + << "for channel: " << grpId.toStdString() << std::endl; + return; + } + + RsThread::async([this, grpId, autoDownload]() + { + if(!rsGxsChannels->setChannelAutoDownload(grpId, !autoDownload)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to set autodownload " + << "for channel: " << grpId.toStdString() << std::endl; + return; + } + +// RsQThreadUtils::postToObject( [=]() +// { +// /* Here it goes any code you want to be executed on the Qt Gui +// * thread, for example to update the data model with new information +// * after a blocking call to RetroShare API complete, note that +// * Qt::QueuedConnection is important! +// */ +// +// std::cerr << __PRETTY_FUNCTION__ << " Has been executed on GUI " +// << "thread but was scheduled by async thread" << std::endl; +// }, this ); + }); +} + +bool GxsChannelPostsWidget::insertGroupData(const RsGxsGenericGroupData *data) +{ + const RsGxsChannelGroup *d = dynamic_cast(data); + + if(!d) + { + RsErr() << __PRETTY_FUNCTION__ << " Cannot dynamic cast input data (" << (void*)data << " to RsGxsGenericGroupData. Something bad happenned." << std::endl; + return false; + } + + insertChannelDetails(*d); + return true; +} + +void GxsChannelPostsWidget::getMsgData(const std::set& msgIds,std::vector& psts) +{ + std::vector posts; + std::vector comments; + std::vector votes; + + rsGxsChannels->getChannelContent( groupId(),msgIds,posts,comments,votes ); + + psts.clear(); + + for(auto& post: posts) + psts.push_back(new RsGxsChannelPost(post)); +} + +void GxsChannelPostsWidget::getAllMsgData(std::vector& psts) +{ + std::vector posts; + std::vector comments; + std::vector votes; + + rsGxsChannels->getChannelAllContent( groupId(),posts,comments,votes ); + + psts.clear(); + + for(auto& post: posts) + psts.push_back(new RsGxsChannelPost(post)); +} + +bool GxsChannelPostsWidget::getGroupData(RsGxsGenericGroupData *& data) +{ + if(groupId().isNull()) + { + RsErr() << __PRETTY_FUNCTION__ << " Trying to get data about null group!!" << std::endl; + return false; + } + std::vector groups; + + if(rsGxsChannels->getChannelsInfo(std::list({groupId()}),groups) && groups.size()==1) + { + data = new RsGxsChannelGroup(groups[0]); + + mGroup = groups[0]; // make a local copy to pass on to items + return true; + } + else + { + RsGxsChannelGroup distant_group; + + if(rsGxsChannels->retrieveDistantGroup(groupId(),distant_group)) + { + insertChannelDetails(distant_group); + data = new RsGxsChannelGroup(distant_group); + mGroup = distant_group; // make a local copy to pass on to items + return true ; + } + } + + return false; +} + +#ifdef TODO +void GxsChannelPostsWidget::insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) +{ + std::vector cposts; + + for(auto post: posts) // This is not so nice but we have somehow to convert to RsGxsChannelPost at some time, and the cposts list is being modified in the insert method. + cposts.push_back(*static_cast(post)); + + insertChannelPosts(cposts, thread, false); +} +#endif + +void GxsChannelPostsWidget::insertPosts(const std::vector& posts) +{ + std::vector cposts; + + for(auto post: posts) // This is not so nice but we have somehow to convert to RsGxsChannelPost at some timer, and the cposts list is being modified in the insert method. + cposts.push_back(*static_cast(post)); + + insertChannelPosts(cposts); +} + +class GxsChannelPostsReadData +{ +public: + GxsChannelPostsReadData(bool read) + { + mRead = read; + mLastToken = 0; + } + +public: + bool mRead; + uint32_t mLastToken; +}; + +static void setAllMessagesReadCallback(FeedItem *feedItem, void *data) +{ + GxsChannelPostItem *channelPostItem = dynamic_cast(feedItem); + if (!channelPostItem) { + return; + } + + GxsChannelPostsReadData *readData = (GxsChannelPostsReadData*) data; + bool isRead = !channelPostItem->isUnread() ; + + if(channelPostItem->isLoaded() && (isRead == readData->mRead)) + return ; + + RsGxsGrpMsgIdPair msgPair = std::make_pair(channelPostItem->groupId(), channelPostItem->messageId()); + rsGxsChannels->setMessageReadStatus(readData->mLastToken, msgPair, readData->mRead); +} + +void GxsChannelPostsWidget::setAllMessagesReadDo(bool read, uint32_t &token) +{ + if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(subscribeFlags())) { + return; + } + + GxsChannelPostsReadData data(read); + ui->feedWidget->withAll(setAllMessagesReadCallback, &data); + + token = data.mLastToken; +} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h new file mode 100644 index 000000000..31fe597a1 --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -0,0 +1,130 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h * + * * + * Copyright 2013 by Robert Fernie * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#ifndef _GXS_CHANNELPOSTSWIDGET_H +#define _GXS_CHANNELPOSTSWIDGET_H + +#include + +#include "gui/gxs/GxsMessageFramePostWidget.h" + +#include "gui/feeds/FeedHolder.h" + +namespace Ui { +class GxsChannelPostsWidget; +} + +class GxsChannelPostItem; +class QTreeWidgetItem; +class FeedItem; +class RsGxsChannelPostsModel; + +class GxsChannelPostsWidgetWithModel: public QWidget, public GxsMessageFrameWidget +{ + Q_OBJECT + +public: + /* Filters */ + enum Filter { + FILTER_TITLE = 1, + FILTER_MSG = 2, + FILTER_FILE_NAME = 3 + }; + +public: + /** Default Constructor */ + GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent = 0); + /** Default Destructor */ + ~GxsChannelPostsWidgetWithModel(); + + /* GxsMessageFrameWidget */ + virtual QIcon groupIcon(); + virtual void groupIdChanged() override; + virtual QString groupName(bool) override; + virtual bool navigate(const RsGxsMessageId&) override; + +#ifdef TODO + /* FeedHolder */ + virtual QScrollArea *getScrollArea(); + virtual void deleteFeedItem(FeedItem *feedItem, uint32_t type); + virtual void openChat(const RsPeerId& peerId); + virtual void openComments(uint32_t type, const RsGxsGroupId &groupId, const QVector &msg_versions, const RsGxsMessageId &msgId, const QString &title); +#endif + +protected: + /* GxsMessageFramePostWidget */ + virtual void groupNameChanged(const QString &name); +#ifdef TODO + virtual bool insertGroupData(const RsGxsGenericGroupData *data) override; +#endif + virtual void clearPosts(); + virtual bool useThread() { return mUseThread; } + virtual void fillThreadCreatePost(const QVariant &post, bool related, int current, int count); + virtual bool navigatePostItem(const RsGxsMessageId& msgId); + virtual void blank() ; + +#ifdef TODO + virtual bool getGroupData(RsGxsGenericGroupData *& data) override; + virtual void getMsgData(const std::set& msgIds,std::vector& posts) override; + virtual void getAllMsgData(std::vector& posts) override; + virtual void insertPosts(const std::vector& posts) override; + virtual void insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) override; +#endif + + /* GxsMessageFrameWidget */ + virtual void setAllMessagesReadDo(bool read, uint32_t &token); + +private slots: + void createMsg(); + void toggleAutoDownload(); + void subscribeGroup(bool subscribe); + void filterChanged(int filter); + void setViewMode(int viewMode); + void settingsChanged(); + +private: + void processSettings(bool load); + + void setAutoDownload(bool autoDl); + static bool filterItem(FeedItem *feedItem, const QString &text, int filter); + + int viewMode(); + + void insertChannelDetails(const RsGxsChannelGroup &group); + void insertChannelPosts(std::vector &posts, GxsMessageFramePostThread *thread, bool related); + + void createPostItem(const RsGxsChannelPost &post, bool related); + void handleEvent_main_thread(std::shared_ptr event); + +private: + QAction *mAutoDownloadAction; + + RsGxsChannelGroup mGroup; + bool mUseThread; + RsEventsHandlerId_t mEventHandlerId ; + + RsGxsChannelPostsModel *mThreadModel; + UIStateHelper *mStateHelper; + + /* UI - from Designer */ + Ui::GxsChannelPostsWidget *ui; +}; + +#endif diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui new file mode 100644 index 000000000..c1bc74996 --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -0,0 +1,563 @@ + + + GxsChannelPostsWidgetWithModel + + + + 0 + 0 + 977 + 628 + + + + + 4 + + + 0 + + + 0 + + + 0 + + + 0 + + + + + QFrame::Box + + + QFrame::Sunken + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + + 64 + 64 + + + + + 64 + 64 + + + + + + + :/images/channels.png + + + true + + + + + + + + + + + + + + Channel Name + + + + + + + + + + QFrame::Box + + + QFrame::Sunken + + + + 4 + + + 2 + + + 6 + + + 2 + + + + + + 0 + 0 + + + + Subscribe + + + + 16 + 16 + + + + QToolButton::MenuButtonPopup + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::NoFocus + + + Post to Channel + + + Add new post + + + + :/icons/png/add.png:/icons/png/add.png + + + + 32 + 16 + + + + Qt::ToolButtonTextBesideIcon + + + true + + + + + + + Qt::Horizontal + + + QSizePolicy::MinimumExpanding + + + + 0 + 0 + + + + + + + + Loading + + + + + + + + 0 + 0 + + + + + 16777215 + 10 + + + + 1000 + + + 0 + + + + + + + 0 + + + + + Show feeds + + + + :/images/view-feeds.png:/images/view-feeds.png + + + true + + + true + + + + + + + Show files + + + + :/images/view-files.png:/images/view-files.png + + + true + + + true + + + + + + + + + + 0 + 0 + + + + + 200 + 0 + + + + + 200 + 16777215 + + + + Search channels + + + true + + + + + + + + + + + + + + 3 + + + 0 + + + 3 + + + 3 + + + + + false + + + QFrame::NoFrame + + + QFrame::Plain + + + + 6 + + + 6 + + + 6 + + + 6 + + + + + Channel details + + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html> + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + true + + + true + + + + + + + + 75 + true + + + + Description: + + + + + + + + 75 + true + + + + Created: + + + + + + + + 75 + true + + + + Administrator: + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Posts: + + + + + + + + 75 + true + + + + Distribution: + + + + + + + unknown + + + + + + + unknown + + + + + + + unknown + + + true + + + + + + + unknown + + + + + + + 0 + + + + + + + + + + + + + Qt::Vertical + + + + 0 + 0 + + + + + + + + + + + + + + Feeds + + + + + Files + + + toolBarFrame + headFrame + infoWidget + postsTree + + + + GxsIdLabel + QLabel +
gui/gxs/GxsIdLabel.h
+
+ + SubscribeToolButton + QToolButton +
gui/common/SubscribeToolButton.h
+
+ + StyledElidedLabel + QLabel +
gui/common/StyledElidedLabel.h
+
+ + LineEditClear + QLineEdit +
gui/common/LineEditClear.h
+
+
+ + + + + +
diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index a5d6838ae..8a1133e02 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -1333,13 +1333,17 @@ gxschannels { gui/gxschannels/GxsChannelGroupDialog.h \ gui/gxschannels/CreateGxsChannelMsg.h \ gui/gxschannels/GxsChannelPostsWidget.h \ + gui/gxschannels/GxsChannelPostsWidgetWithModel.h \ + gui/gxschannels/GxsChannelPostsModel.h \ gui/gxschannels/GxsChannelFilesWidget.h \ gui/gxschannels/GxsChannelFilesStatusWidget.h \ gui/feeds/GxsChannelGroupItem.h \ gui/feeds/GxsChannelPostItem.h \ gui/gxschannels/GxsChannelUserNotify.h - FORMS += gui/gxschannels/GxsChannelPostsWidget.ui \ + FORMS += \ + gui/gxschannels/GxsChannelPostsWidgetWithModel.ui \ + gui/gxschannels/GxsChannelPostsWidget.ui \ gui/gxschannels/GxsChannelFilesWidget.ui \ gui/gxschannels/GxsChannelFilesStatusWidget.ui \ gui/gxschannels/CreateGxsChannelMsg.ui \ @@ -1348,6 +1352,8 @@ gxschannels { SOURCES += gui/gxschannels/GxsChannelDialog.cpp \ gui/gxschannels/GxsChannelPostsWidget.cpp \ + gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp \ + gui/gxschannels/GxsChannelPostsModel.cpp \ gui/gxschannels/GxsChannelFilesWidget.cpp \ gui/gxschannels/GxsChannelFilesStatusWidget.cpp \ gui/gxschannels/GxsChannelGroupDialog.cpp \ From 5a6c8de0056fb7fd40e7de30c647e2001d04cbda Mon Sep 17 00:00:00 2001 From: csoler Date: Tue, 2 Jun 2020 22:22:36 +0200 Subject: [PATCH 02/89] fixed abstract item model for channels. Display still missing --- .../gui/gxschannels/GxsChannelFilesWidget.cpp | 9 - .../gui/gxschannels/GxsChannelPostsWidget.h | 2 +- .../GxsChannelPostsWidgetWithModel.cpp | 271 ++++++++---------- .../GxsChannelPostsWidgetWithModel.h | 19 +- .../gui/gxsforums/GxsForumThreadWidget.cpp | 1 + 5 files changed, 134 insertions(+), 168 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp index 3afb4e645..5ae905cea 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp @@ -80,15 +80,6 @@ GxsChannelFilesWidget::GxsChannelFilesWidget(QWidget *parent) : ui->treeWidget->setColumnWidth(COLUMN_PUBLISHED, 150); } -GxsChannelFilesWidget::paintEvent() -{ - QWidget::paintEvent(); - - if(!mLoaded) - { - } -} - GxsChannelFilesWidget::~GxsChannelFilesWidget() { delete(mCompareRole); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h index 28a521944..dded41491 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidget.h @@ -98,7 +98,7 @@ private: int viewMode(); void insertChannelDetails(const RsGxsChannelGroup &group); - void insertChannelPosts(std::vector &posts); + void insertChannelPosts(std::vector& posts, GxsMessageFramePostThread *thread, bool related); void createPostItem(const RsGxsChannelPost &post, bool related); void handleEvent_main_thread(std::shared_ptr event); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index bc3c564e3..dd2c8cccc 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -24,7 +24,8 @@ #include "retroshare/rsgxscircles.h" #include "GxsChannelPostsWidgetWithModel.h" -#include "ui_GxsChannelPostsWidget.h" +#include "GxsChannelPostsModel.h" +#include "ui_GxsChannelPostsWidgetWithModel.h" #include "gui/feeds/GxsChannelPostItem.h" #include "gui/gxs/GxsIdDetails.h" #include "gui/gxschannels/CreateGxsChannelMsg.h" @@ -53,27 +54,27 @@ /** Constructor */ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : - GxsMessageFramePostWidget(rsGxsChannels, parent), - ui(new Ui::GxsChannelPostsWidget) + GxsMessageFrameWidget(rsGxsChannels, parent), + ui(new Ui::GxsChannelPostsWidgetWithModel) { /* Invoke the Qt Designer generated object setup routine */ ui->setupUi(this); - ui->postsTree->setModel(new RsGxsChannelPostsModel()); + ui->postsTree->setModel(mThreadModel = new RsGxsChannelPostsModel()); /* Setup UI helper */ - mStateHelper->addWidget(mTokenTypeAllPosts, ui->progressBar, UISTATE_LOADING_VISIBLE); - mStateHelper->addWidget(mTokenTypeAllPosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); - mStateHelper->addWidget(mTokenTypeAllPosts, ui->filterLineEdit); + //mStateHelper->addWidget(mTokenTypeAllPosts, ui->progressBar, UISTATE_LOADING_VISIBLE); + //mStateHelper->addWidget(mTokenTypeAllPosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); + //mStateHelper->addWidget(mTokenTypeAllPosts, ui->filterLineEdit); - mStateHelper->addWidget(mTokenTypePosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); + //mStateHelper->addWidget(mTokenTypePosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); - mStateHelper->addLoadPlaceholder(mTokenTypeGroupData, ui->nameLabel); + //mStateHelper->addLoadPlaceholder(mTokenTypeGroupData, ui->nameLabel); - mStateHelper->addWidget(mTokenTypeGroupData, ui->postButton); - mStateHelper->addWidget(mTokenTypeGroupData, ui->logoLabel); - mStateHelper->addWidget(mTokenTypeGroupData, ui->subscribeToolButton); + //mStateHelper->addWidget(mTokenTypeGroupData, ui->postButton); + //mStateHelper->addWidget(mTokenTypeGroupData, ui->logoLabel); + //mStateHelper->addWidget(mTokenTypeGroupData, ui->subscribeToolButton); /* Connect signals */ connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg())); @@ -111,8 +112,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->nameLabel->setMinimumWidth(20); /* Initialize feed widget */ - ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); - ui->feedWidget->setFilterCallback(filterItem); + //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); + //ui->feedWidget->setFilterCallback(filterItem); /* load settings */ processSettings(true); @@ -141,7 +142,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI mEventHandlerId, RsEventType::GXS_CHANNELS ); } -void GxsChannelPostsWidget::handleEvent_main_thread(std::shared_ptr event) +void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr event) { const RsGxsChannelEvent *e = dynamic_cast(event.get()); @@ -157,29 +158,90 @@ void GxsChannelPostsWidget::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) updateDisplay(true); break; - case RsChannelEventCode::READ_STATUS_CHANGED: - if (FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(e->mChannelMsgId))) - if (GxsChannelPostItem *channelPostItem = dynamic_cast(feedItem)) - channelPostItem->setReadStatus(false,!channelPostItem->isUnread()); - //channelPostItem->setReadStatus(false,e->Don't get read status. Will be more easier and accurate); - break; +// case RsChannelEventCode::READ_STATUS_CHANGED: +// if (FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(e->mChannelMsgId))) +// if (GxsChannelPostItem *channelPostItem = dynamic_cast(feedItem)) +// channelPostItem->setReadStatus(false,!channelPostItem->isUnread()); +// //channelPostItem->setReadStatus(false,e->Don't get read status. Will be more easier and accurate); +// break; default: break; } } -GxsChannelPostsWidget::~GxsChannelPostsWidget() +void GxsChannelPostsWidgetWithModel::updateGroupData() +{ + if(groupId().isNull()) + return; + + RsThread::async([this]() + { + std::vector groups; + + if(!rsGxsChannels->getChannelsInfo(std::list{ groupId() }, groups)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to get autodownload value 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]() + { + mGroup = groups[0]; + mThreadModel->updateChannel(groupId()); + } ); + }); +} + +void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete) +{ +#ifdef DEBUG_CHANNEL + std::cerr << "udateDisplay: groupId()=" << groupId()<< std::endl; +#endif + if(groupId().isNull()) + { +#ifdef DEBUG_CHANNEL + std::cerr << " group_id=0. Return!"<< std::endl; +#endif + return; + } + + if(mGroup.mMeta.mGroupId.isNull() && !groupId().isNull()) + { +#ifdef DEBUG_FORUMS + std::cerr << " inconsistent group data. Reloading!"<< std::endl; +#endif + complete = true; + } + if(complete) // need to update the group data, reload the messages etc. + { +#warning todo + //saveExpandedItems(mSavedExpandedMessages); + + //if(mGroupId != mThreadModel->currentGroupId()) + // mThreadId.clear(); + + updateGroupData(); + + return; + } +} +GxsChannelPostsWidgetWithModel::~GxsChannelPostsWidgetWithModel() { rsEvents->unregisterEventsHandler(mEventHandlerId); // save settings processSettings(false); delete(mAutoDownloadAction); - delete ui; } -void GxsChannelPostsWidget::processSettings(bool load) +void GxsChannelPostsWidgetWithModel::processSettings(bool load) { Settings->beginGroup(QString("ChannelPostsWidget")); @@ -204,14 +266,19 @@ void GxsChannelPostsWidget::processSettings(bool load) Settings->endGroup(); } -void GxsChannelPostsWidget::settingsChanged() +void GxsChannelPostsWidgetWithModel::settingsChanged() { mUseThread = Settings->getChannelLoadThread(); - mStateHelper->setWidgetVisible(ui->progressBar, mUseThread); + //mStateHelper->setWidgetVisible(ui->progressBar, mUseThread); } -void GxsChannelPostsWidget::groupNameChanged(const QString &name) +QString GxsChannelPostsWidgetWithModel::groupName(bool) +{ + return "Group name" ; +} + +void GxsChannelPostsWidgetWithModel::groupNameChanged(const QString &name) { if (groupId().isNull()) { ui->nameLabel->setText(tr("No Channel Selected")); @@ -221,11 +288,11 @@ void GxsChannelPostsWidget::groupNameChanged(const QString &name) } } -QIcon GxsChannelPostsWidget::groupIcon() +QIcon GxsChannelPostsWidgetWithModel::groupIcon() { - if (mStateHelper->isLoading(mTokenTypeGroupData) || mStateHelper->isLoading(mTokenTypeAllPosts)) { - return QIcon(":/images/kalarm.png"); - } +// if (mStateHelper->isLoading(mTokenTypeGroupData) || mStateHelper->isLoading(mTokenTypeAllPosts)) { +// return QIcon(":/images/kalarm.png"); +// } // if (mNewCount) { // return QIcon(":/images/message-state-new.png"); @@ -238,36 +305,19 @@ QIcon GxsChannelPostsWidget::groupIcon() /*************************************************************************************/ /*************************************************************************************/ -QScrollArea *GxsChannelPostsWidget::getScrollArea() -{ - return NULL; -} - -void GxsChannelPostsWidget::deleteFeedItem(FeedItem *feedItem, uint32_t /*type*/) -{ - if (!feedItem) - return; - - ui->feedWidget->removeFeedItem(feedItem); -} - -void GxsChannelPostsWidget::openChat(const RsPeerId & /*peerId*/) -{ -} - // Callback from Widget->FeedHolder->ServiceDialog->CommentContainer->CommentDialog, -void GxsChannelPostsWidget::openComments(uint32_t /*type*/, const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title) +void GxsChannelPostsWidgetWithModel::openComments(uint32_t /*type*/, const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title) { emit loadComment(groupId, msg_versions,msgId, title); } -void GxsChannelPostsWidget::createMsg() +void GxsChannelPostsWidgetWithModel::createMsg() { if (groupId().isNull()) { return; } - if (!IS_GROUP_SUBSCRIBED(subscribeFlags())) { + if (!IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { return; } @@ -277,7 +327,7 @@ void GxsChannelPostsWidget::createMsg() /* window will destroy itself! */ } -void GxsChannelPostsWidget::insertChannelDetails(const RsGxsChannelGroup &group) +void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGroup &group) { /* IMAGE */ QPixmap chanImage; @@ -387,7 +437,7 @@ void GxsChannelPostsWidget::insertChannelDetails(const RsGxsChannelGroup &group) } } -int GxsChannelPostsWidget::viewMode() +int GxsChannelPostsWidgetWithModel::viewMode() { if (ui->feedToolButton->isChecked()) { return VIEW_MODE_FEEDS; @@ -399,7 +449,7 @@ int GxsChannelPostsWidget::viewMode() return VIEW_MODE_FEEDS; } -void GxsChannelPostsWidget::setViewMode(int viewMode) +void GxsChannelPostsWidgetWithModel::setViewMode(int viewMode) { #ifdef TODO switch (viewMode) { @@ -426,7 +476,7 @@ void GxsChannelPostsWidget::setViewMode(int viewMode) #endif } -void GxsChannelPostsWidget::filterChanged(int filter) +void GxsChannelPostsWidgetWithModel::filterChanged(int filter) { #ifdef TODO ui->feedWidget->setFilterType(filter); @@ -434,7 +484,8 @@ void GxsChannelPostsWidget::filterChanged(int filter) #endif } -/*static*/ bool GxsChannelPostsWidget::filterItem(FeedItem *feedItem, const QString &text, int filter) +#ifdef TODO +/*static*/ bool GxsChannelPostsWidgetWithModel::filterItem(FeedItem *feedItem, const QString &text, int filter) { GxsChannelPostItem *item = dynamic_cast(feedItem); if (!item) { @@ -474,7 +525,6 @@ void GxsChannelPostsWidget::filterChanged(int filter) return bVisible; } -#ifdef TODO void GxsChannelPostsWidget::createPostItemFromMetaData(const RsGxsMsgMetaData& meta,bool related) { GxsChannelPostItem *item = NULL; @@ -518,14 +568,7 @@ void GxsChannelPostsWidget::createPostItemFromMetaData(const RsGxsMsgMetaData& m ui->fileWidget->addFiles(post, related); #endif } -#endif -void GxsChannelPostsWidget::insertChannelPosts(std::vector& posts) -{ - mModel->setPosts(posts); -} - -#ifdef TODO void GxsChannelPostsWidget::createPostItem(const RsGxsChannelPost& post, bool related) { GxsChannelPostItem *item = NULL; @@ -734,31 +777,25 @@ void GxsChannelPostsWidget::clearPosts() } #endif -void GxsChannelPostsWidget::blank() +void GxsChannelPostsWidgetWithModel::blank() { - mStateHelper->setWidgetEnabled(ui->postButton, false); - mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); + //mStateHelper->setWidgetEnabled(ui->postButton, false); + //mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); - clearPosts(); - + mThreadModel->clear(); groupNameChanged(QString()); ui->infoWidget->hide(); - ui->feedWidget->show(); - ui->fileWidget->hide(); } -bool GxsChannelPostsWidget::navigatePostItem(const RsGxsMessageId &msgId) +bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId &msgId) { - FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(msgId)); - if (!feedItem) { - return false; - } - - return ui->feedWidget->scrollTo(feedItem, true); +#warning TODO + //return ui->feedWidget->scrollTo(feedItem, true); + return true; } -void GxsChannelPostsWidget::subscribeGroup(bool subscribe) +void GxsChannelPostsWidgetWithModel::subscribeGroup(bool subscribe) { RsGxsGroupId grpId(groupId()); if (grpId.isNull()) return; @@ -769,13 +806,13 @@ void GxsChannelPostsWidget::subscribeGroup(bool subscribe) } ); } -void GxsChannelPostsWidget::setAutoDownload(bool autoDl) +void GxsChannelPostsWidgetWithModel::setAutoDownload(bool autoDl) { mAutoDownloadAction->setChecked(autoDl); mAutoDownloadAction->setText(autoDl ? tr("Disable Auto-Download") : tr("Enable Auto-Download")); } -void GxsChannelPostsWidget::toggleAutoDownload() +void GxsChannelPostsWidgetWithModel::toggleAutoDownload() { RsGxsGroupId grpId = groupId(); if (grpId.isNull()) { @@ -813,7 +850,8 @@ void GxsChannelPostsWidget::toggleAutoDownload() }); } -bool GxsChannelPostsWidget::insertGroupData(const RsGxsGenericGroupData *data) +#ifdef TODO +bool GxsChannelPostsWidgetWithModel::insertGroupData(const RsGxsGenericGroupData *data) { const RsGxsChannelGroup *d = dynamic_cast(data); @@ -827,67 +865,6 @@ bool GxsChannelPostsWidget::insertGroupData(const RsGxsGenericGroupData *data) return true; } -void GxsChannelPostsWidget::getMsgData(const std::set& msgIds,std::vector& psts) -{ - std::vector posts; - std::vector comments; - std::vector votes; - - rsGxsChannels->getChannelContent( groupId(),msgIds,posts,comments,votes ); - - psts.clear(); - - for(auto& post: posts) - psts.push_back(new RsGxsChannelPost(post)); -} - -void GxsChannelPostsWidget::getAllMsgData(std::vector& psts) -{ - std::vector posts; - std::vector comments; - std::vector votes; - - rsGxsChannels->getChannelAllContent( groupId(),posts,comments,votes ); - - psts.clear(); - - for(auto& post: posts) - psts.push_back(new RsGxsChannelPost(post)); -} - -bool GxsChannelPostsWidget::getGroupData(RsGxsGenericGroupData *& data) -{ - if(groupId().isNull()) - { - RsErr() << __PRETTY_FUNCTION__ << " Trying to get data about null group!!" << std::endl; - return false; - } - std::vector groups; - - if(rsGxsChannels->getChannelsInfo(std::list({groupId()}),groups) && groups.size()==1) - { - data = new RsGxsChannelGroup(groups[0]); - - mGroup = groups[0]; // make a local copy to pass on to items - return true; - } - else - { - RsGxsChannelGroup distant_group; - - if(rsGxsChannels->retrieveDistantGroup(groupId(),distant_group)) - { - insertChannelDetails(distant_group); - data = new RsGxsChannelGroup(distant_group); - mGroup = distant_group; // make a local copy to pass on to items - return true ; - } - } - - return false; -} - -#ifdef TODO void GxsChannelPostsWidget::insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) { std::vector cposts; @@ -897,7 +874,6 @@ void GxsChannelPostsWidget::insertAllPosts(const std::vector& posts) { @@ -908,6 +884,7 @@ void GxsChannelPostsWidget::insertPosts(const std::vector& insertChannelPosts(cposts); } +#endif class GxsChannelPostsReadData { @@ -940,14 +917,14 @@ static void setAllMessagesReadCallback(FeedItem *feedItem, void *data) rsGxsChannels->setMessageReadStatus(readData->mLastToken, msgPair, readData->mRead); } -void GxsChannelPostsWidget::setAllMessagesReadDo(bool read, uint32_t &token) +void GxsChannelPostsWidgetWithModel::setAllMessagesReadDo(bool read, uint32_t &token) { - if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(subscribeFlags())) { + if (groupId().isNull() || !IS_GROUP_SUBSCRIBED(mGroup.mMeta.mSubscribeFlags)) { return; } GxsChannelPostsReadData data(read); - ui->feedWidget->withAll(setAllMessagesReadCallback, &data); + //ui->feedWidget->withAll(setAllMessagesReadCallback, &data); token = data.mLastToken; } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 31fe597a1..64478a112 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -28,7 +28,7 @@ #include "gui/feeds/FeedHolder.h" namespace Ui { -class GxsChannelPostsWidget; +class GxsChannelPostsWidgetWithModel; } class GxsChannelPostItem; @@ -36,7 +36,7 @@ class QTreeWidgetItem; class FeedItem; class RsGxsChannelPostsModel; -class GxsChannelPostsWidgetWithModel: public QWidget, public GxsMessageFrameWidget +class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget { Q_OBJECT @@ -56,17 +56,19 @@ public: /* GxsMessageFrameWidget */ virtual QIcon groupIcon(); - virtual void groupIdChanged() override; + virtual void groupIdChanged() { updateDisplay(true); } virtual QString groupName(bool) override; virtual bool navigate(const RsGxsMessageId&) override; + void updateDisplay(bool complete); + #ifdef TODO /* FeedHolder */ virtual QScrollArea *getScrollArea(); virtual void deleteFeedItem(FeedItem *feedItem, uint32_t type); virtual void openChat(const RsPeerId& peerId); - virtual void openComments(uint32_t type, const RsGxsGroupId &groupId, const QVector &msg_versions, const RsGxsMessageId &msgId, const QString &title); #endif + virtual void openComments(uint32_t type, const RsGxsGroupId &groupId, const QVector &msg_versions, const RsGxsMessageId &msgId, const QString &title); protected: /* GxsMessageFramePostWidget */ @@ -74,10 +76,7 @@ protected: #ifdef TODO virtual bool insertGroupData(const RsGxsGenericGroupData *data) override; #endif - virtual void clearPosts(); virtual bool useThread() { return mUseThread; } - virtual void fillThreadCreatePost(const QVariant &post, bool related, int current, int count); - virtual bool navigatePostItem(const RsGxsMessageId& msgId); virtual void blank() ; #ifdef TODO @@ -92,6 +91,7 @@ protected: virtual void setAllMessagesReadDo(bool read, uint32_t &token); private slots: + void updateGroupData(); void createMsg(); void toggleAutoDownload(); void subscribeGroup(bool subscribe); @@ -108,9 +108,6 @@ private: int viewMode(); void insertChannelDetails(const RsGxsChannelGroup &group); - void insertChannelPosts(std::vector &posts, GxsMessageFramePostThread *thread, bool related); - - void createPostItem(const RsGxsChannelPost &post, bool related); void handleEvent_main_thread(std::shared_ptr event); private: @@ -124,7 +121,7 @@ private: UIStateHelper *mStateHelper; /* UI - from Designer */ - Ui::GxsChannelPostsWidget *ui; + Ui::GxsChannelPostsWidgetWithModel *ui; }; #endif diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp index 02eb74334..5e7e481b3 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp @@ -498,6 +498,7 @@ void GxsForumThreadWidget::recursRestoreExpandedItems(const QModelIndex& /*index ui->threadTreeWidget->setExpanded( mThreadProxyModel->mapFromSource(mThreadModel->getIndexOfMessage(*it)) ,true) ; } + void GxsForumThreadWidget::updateDisplay(bool complete) { #ifdef DEBUG_FORUMS From da968379d6f56a33cad0811970b2418641609f27 Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 3 Jun 2020 21:06:37 +0200 Subject: [PATCH 03/89] improved design: moved channel details+posts into tabs, and added tabs for comments+post details --- .../GxsChannelPostsWidgetWithModel.cpp | 113 +++- .../GxsChannelPostsWidgetWithModel.h | 22 +- .../GxsChannelPostsWidgetWithModel.ui | 501 ++++++++---------- 3 files changed, 332 insertions(+), 304 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index dd2c8cccc..ad695136d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -20,6 +20,7 @@ #include #include +#include #include "retroshare/rsgxscircles.h" @@ -52,6 +53,86 @@ #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 +Q_DECLARE_METATYPE(RsGxsChannelPost*) + +void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + QString byteUnits[4] = {tr("B"), tr("KB"), tr("MB"), tr("GB")}; + QStyleOptionViewItem opt = option; + QStyleOptionProgressBarV2 newopt; + QRect pixmapRect; + QPixmap pixmap; + qlonglong fileSize; + double dlspeed, multi; + int seconds,minutes, hours, days; + qlonglong remaining; + QString temp ; + qlonglong completed; + qlonglong downloadtime; + qint64 qi64Value; + + // prepare + painter->save(); + painter->setClipRect(opt.rect); + + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + //const RsGxsChannelPost& post(*pinfo); + + QVariant value = index.data(Qt::TextColorRole); + + if(value.isValid() && qvariant_cast(value).isValid()) + opt.palette.setColor(QPalette::Text, qvariant_cast(value)); + + QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + + if(option.state & QStyle::State_Selected) + painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); + else + painter->setPen(opt.palette.color(cg, QPalette::Text)); + + //painter->drawText(option.rect, Qt::AlignRight, QString("TODO")); + + QPixmap thumbnail; + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); + + QFontMetricsF fm(opt.font); + + int W = IMAGE_SIZE_FACTOR_W * fm.height() * IMAGE_ZOOM_FACTOR; + int H = IMAGE_SIZE_FACTOR_H * fm.height() * IMAGE_ZOOM_FACTOR; + + int w = fm.width("X") ; + int h = fm.height() ; + + float img_coord_x = IMAGE_MARGIN_FACTOR*0.5*w; + float img_coord_y = IMAGE_MARGIN_FACTOR*0.5*h; + + QPoint img_pos(img_coord_x,img_coord_y); + QPoint img_size(W,H); + + QPoint txt_pos(0,img_coord_y + H + h); + + painter->drawPixmap(QRect(opt.rect.topLeft() + img_pos,opt.rect.topLeft()+img_pos+img_size),thumbnail.scaled(W,H,Qt::KeepAspectRatio,Qt::SmoothTransformation)); + painter->drawText(QRect(option.rect.topLeft() + txt_pos,option.rect.bottomRight()),Qt::AlignCenter,QString::fromStdString(post.mMeta.mMsgName)); +} + +QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + //QPixmap thumbnail; + //GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); + + QFontMetricsF fm(option.font); + + int W = IMAGE_SIZE_FACTOR_W * fm.height() * IMAGE_ZOOM_FACTOR; + int H = IMAGE_SIZE_FACTOR_H * fm.height() * IMAGE_ZOOM_FACTOR; + + int h = fm.height() ; + int w = fm.width("X") ; + + return QSize(W+IMAGE_MARGIN_FACTOR*w,H + 2*h); +} + /** Constructor */ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : GxsMessageFrameWidget(rsGxsChannels, parent), @@ -61,6 +142,9 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->setupUi(this); ui->postsTree->setModel(mThreadModel = new RsGxsChannelPostsModel()); + ui->postsTree->setItemDelegate(new ChannelPostDelegate()); + + connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); /* Setup UI helper */ @@ -95,7 +179,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI /* Initialize view button */ //setViewMode(VIEW_MODE_FEEDS); see processSettings - ui->infoWidget->hide(); + //ui->infoWidget->hide(); QSignalMapper *signalMapper = new QSignalMapper(this); connect(ui->feedToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); @@ -109,7 +193,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->loadingLabel->hide(); ui->progressBar->hide(); - ui->nameLabel->setMinimumWidth(20); + //ui->nameLabel->setMinimumWidth(20); /* Initialize feed widget */ //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); @@ -169,6 +253,13 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrpostsTree->selectionModel()->currentIndex(); + + std::cerr << "Showing details about selected index : "<< selected_index.row() << "," << selected_index.column() << std::endl; +} + void GxsChannelPostsWidgetWithModel::updateGroupData() { if(groupId().isNull()) @@ -280,12 +371,12 @@ QString GxsChannelPostsWidgetWithModel::groupName(bool) void GxsChannelPostsWidgetWithModel::groupNameChanged(const QString &name) { - if (groupId().isNull()) { - ui->nameLabel->setText(tr("No Channel Selected")); - ui->logoLabel->setPixmap(QPixmap(":/icons/png/channels.png")); - } else { - ui->nameLabel->setText(name); - } +// if (groupId().isNull()) { +// ui->nameLabel->setText(tr("No Channel Selected")); +// ui->logoLabel->setPixmap(QPixmap(":/icons/png/channels.png")); +// } else { +// ui->nameLabel->setText(name); +// } } QIcon GxsChannelPostsWidgetWithModel::groupIcon() @@ -336,7 +427,7 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou } else { chanImage = QPixmap(CHAN_DEFAULT_IMAGE); } - ui->logoLabel->setPixmap(chanImage); + //ui->logoLabel->setPixmap(chanImage); if (group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH) { @@ -361,7 +452,7 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou ui->feedToolButton->setEnabled(true); ui->fileToolButton->setEnabled(true); - ui->infoWidget->hide(); + //ui->infoWidget->hide(); setViewMode(viewMode()); ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) ); @@ -785,7 +876,7 @@ void GxsChannelPostsWidgetWithModel::blank() mThreadModel->clear(); groupNameChanged(QString()); - ui->infoWidget->hide(); + //ui->infoWidget->hide(); } bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId &msgId) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 64478a112..246be0715 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -23,8 +23,9 @@ #include -#include "gui/gxs/GxsMessageFramePostWidget.h" +#include +#include "gui/gxs/GxsMessageFramePostWidget.h" #include "gui/feeds/FeedHolder.h" namespace Ui { @@ -36,6 +37,24 @@ class QTreeWidgetItem; class FeedItem; class RsGxsChannelPostsModel; +class ChannelPostDelegate: public QAbstractItemDelegate +{ + Q_OBJECT + + public: + ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent){} + virtual ~ChannelPostDelegate(){} + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + private: + static constexpr float IMAGE_MARGIN_FACTOR = 1.0; + static constexpr float IMAGE_SIZE_FACTOR_W = 4.0 ; + static constexpr float IMAGE_SIZE_FACTOR_H = 6.0 ; + static constexpr float IMAGE_ZOOM_FACTOR = 1.0; +}; + class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget { Q_OBJECT @@ -91,6 +110,7 @@ protected: virtual void setAllMessagesReadDo(bool read, uint32_t &token); private slots: + void showPostDetails(); void updateGroupData(); void createMsg(); void toggleAutoDownload(); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index c1bc74996..2ae7ecac8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -26,69 +26,6 @@ 0 - - - - QFrame::Box - - - QFrame::Sunken - - - - 4 - - - 4 - - - 4 - - - 4 - - - - - - 64 - 64 - - - - - 64 - 64 - - - - - - - :/images/channels.png - - - true - - - - - - - - - - - - - - Channel Name - - - - - - @@ -285,237 +222,226 @@ - - - + + + 0 - - - 3 - - - 0 - - - 3 - - - 3 - - - - - false - - - QFrame::NoFrame - - - QFrame::Plain - - - - 6 + + + Channel details + + + + + + Channel details - - 6 - - - 6 - - - 6 - - - - - Channel details - - - - 9 + + + 9 + + + 9 + + + 9 + + + 9 + + + + + + 0 + 0 + - - 9 + + + 75 + true + - - 9 + + Last Post: - - 9 - - - - - - 0 - 0 - - - - - 75 - true - - - - Last Post: - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - true - - - true - - - - - - - - 75 - true - - - - Description: - - - - - - - - 75 - true - - - - Created: - - - - - - - - 75 - true - - - - Administrator: - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Posts: - - - - - - - - 75 - true - - - - Distribution: - - - - - - - unknown - - - - - - - unknown - - - - - - - unknown - - - true - - - - - - - unknown - - - - - - - 0 - - - - - - - - - - - - - Qt::Vertical - - - - 0 - 0 - - - - - + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + true + + + true + + + + + + + + 75 + true + + + + Description: + + + + + + + + 75 + true + + + + Created: + + + + + + + + 75 + true + + + + Administrator: + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Posts: + + + + + + + + 75 + true + + + + Distribution: + + + + + + + unknown + + + + + + + unknown + + + + + + + unknown + + + true + + + + + + + unknown + + + + + + + 0 + + + + + + + + + + + Posts + + + + + + QAbstractItemView::SelectItems + + + false + + + + + - + + + 0 + + + + Details + + + + + + + + + + Comments + + + @@ -528,10 +454,6 @@ p, li { white-space: pre-wrap; } Files - toolBarFrame - headFrame - infoWidget - postsTree @@ -544,11 +466,6 @@ p, li { white-space: pre-wrap; } QToolButton
gui/common/SubscribeToolButton.h
- - StyledElidedLabel - QLabel -
gui/common/StyledElidedLabel.h
-
LineEditClear QLineEdit @@ -556,8 +473,8 @@ p, li { white-space: pre-wrap; }
- + From a38d5aba141ae8f1930a090dd898eed7f39c13c3 Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 3 Jun 2020 22:49:13 +0200 Subject: [PATCH 04/89] added missing files --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 1104 +++++++++++++++++ .../gui/gxschannels/GxsChannelPostsModel.h | 227 ++++ 2 files changed, 1331 insertions(+) create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp new file mode 100644 index 000000000..5d82110d5 --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -0,0 +1,1104 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp * + * * + * Copyright 2020 by Cyril Soler * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "gui/common/FilesDefs.h" +#include "util/qtthreadsutils.h" +#include "util/HandleRichText.h" +#include "util/DateTime.h" +#include "GxsChannelPostsModel.h" +#include "retroshare/rsgxsflags.h" +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsexpr.h" + +#define DEBUG_CHANNEL_MODEL + +Q_DECLARE_METATYPE(RsMsgMetaData) + +Q_DECLARE_METATYPE(RsGxsChannelPost) + +std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere + +const QString RsGxsChannelPostsModel::FilterString("filtered"); + +RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent) + : QAbstractItemModel(parent), mTreeMode(TREE_MODE_PLAIN), mColumns(6) +{ + initEmptyHierarchy(mPosts); +} + +void RsGxsChannelPostsModel::initEmptyHierarchy(std::vector& posts) +{ + preMods(); + + posts.resize(1); // adds a sentinel item + posts[0].mMeta.mMsgName = "Root sentinel post" ; + + postMods(); +} + +void RsGxsChannelPostsModel::preMods() +{ + //emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev. + + beginResetModel(); +} +void RsGxsChannelPostsModel::postMods() +{ + endResetModel(); + + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mPosts.size(),mColumns-1,(void*)NULL)); +} + +void RsGxsChannelPostsModel::setTreeMode(TreeMode mode) +{ + if(mode == mTreeMode) + return; + + preMods(); + + // We're not removing/adding rows here. We're simply asking for re-draw. + + mTreeMode = mode; + postMods(); +} + +#ifdef TODO +void RsGxsChannelPostsModel::setSortMode(SortMode mode) +{ + preMods(); + + mSortMode = mode; + + postMods(); +} + +void RsGxsForumModel::initEmptyHierarchy(std::vector& posts) +{ + preMods(); + + posts.resize(1); // adds a sentinel item + posts[0].mTitle = "Root sentinel post" ; + posts[0].mParent = 0; + + postMods(); +} +#endif + +int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const +{ + if(parent.column() > 0) + return 0; + + if(mPosts.empty()) // security. Should never happen. + return 0; + + if(!parent.isValid()) + return (getChildrenCount(0) + mColumns-1)/mColumns; + + RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; + return 0; + + //else + // return getChildrenCount(parent.internalId()); +} + +int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const +{ + return mColumns ; +} + +// std::vector > RsGxsChannelPostsModel::getPostVersions(const RsGxsMessageId& mid) const +// { +// auto it = mPostVersions.find(mid); +// +// if(it != mPostVersions.end()) +// return it->second; +// else +// return std::vector >(); +// } + +bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const +{ + if(!i.isValid()) + return true; + + quintptr ref = i.internalId(); + uint32_t entry = 0; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) + return false ; + + fmpe = mPosts[entry]; + + return true; + +} + +bool RsGxsChannelPostsModel::hasChildren(const QModelIndex &parent) const +{ + if(!parent.isValid()) + return true; + + return false; // by default, no channel post has children +} + +bool RsGxsChannelPostsModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref) +{ + // the pointer is formed the following way: + // + // [ 32 bits ] + // + // This means that the whole software has the following build-in limitation: + // * 4 B simultaenous posts. Should be enough ! + + ref = (intptr_t)entry; + + return true; +} + +bool RsGxsChannelPostsModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry) +{ + intptr_t val = (intptr_t)ref; + + if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious + { + RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl; + return false ; + } + entry = quintptr(val); + + return true; +} + +QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex & parent) const +{ + if(row < 0 || column < 0 || column >= mColumns) + return QModelIndex(); + + quintptr ref = getChildRef(parent.internalId(),row + column*mColumns); + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl; +#endif + return createIndex(row,column,ref) ; +} + +QModelIndex RsGxsChannelPostsModel::parent(const QModelIndex& index) const +{ + if(!index.isValid()) + return QModelIndex(); + + return QModelIndex(); // there's no hierarchy here. So nothing to do! +} + +Qt::ItemFlags RsGxsChannelPostsModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) + return 0; + + return QAbstractItemModel::flags(index); +} + +quintptr RsGxsChannelPostsModel::getChildRef(quintptr ref,int index) const +{ + if (index < 0) + return 0; + + ChannelPostsModelIndex entry ; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) + return 0 ; + + if(entry == 0) + { + quintptr new_ref; + convertTabEntryToRefPointer(index+1,new_ref); + return new_ref; + } + else + return 0 ; +} + +quintptr RsGxsChannelPostsModel::getParentRow(quintptr ref,int& row) const +{ + ChannelPostsModelIndex ref_entry; + + if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mPosts.size()) + return 0 ; + + if(ref_entry == 0) + { + RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl; + row = 0; + } + else + row = ref_entry-1; + + return 0; +} + +int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const +{ + uint32_t entry = 0 ; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) + return 0 ; + + if(entry == 0) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Children count (flat mode): " << mPosts.size()-1 << std::endl; +#endif + return ((int)mPosts.size())-1; + } + else + return 0; +} + +QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const +{ +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "calling data(" << index << ") role=" << role << std::endl; +#endif + + if(!index.isValid()) + return QVariant(); + + switch(role) + { + case Qt::SizeHintRole: return sizeHintRole(index.column()) ; + case Qt::StatusTipRole:return QVariant(); + default: break; + } + + quintptr ref = (index.isValid())?index.internalId():0 ; + uint32_t entry = 0; + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "data(" << index << ")" ; +#endif + + if(!ref) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " [empty]" << std::endl; +#endif + return QVariant() ; + } + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Bad pointer: " << (void*)ref << std::endl; +#endif + return QVariant() ; + } + + const RsGxsChannelPost& fmpe(mPosts[entry]); + +#ifdef TODO + if(role == Qt::FontRole) + { + QFont font ; + font.setBold( (fmpe.mPostFlags & (ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN | ForumModelPostEntry::FLAG_POST_IS_PINNED)) || IS_MSG_UNREAD(fmpe.mMsgStatus)); + return QVariant(font); + } + + if(role == UnreadChildrenRole) + return bool(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN); + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " [ok]" << std::endl; +#endif +#endif + + switch(role) + { + case Qt::DisplayRole: return displayRole (fmpe,index.column()) ; + case Qt::UserRole: return userRole (fmpe,index.column()) ; +#ifdef TODO + case Qt::DecorationRole: return decorationRole(fmpe,index.column()) ; + case Qt::ToolTipRole: return toolTipRole (fmpe,index.column()) ; + case Qt::TextColorRole: return textColorRole (fmpe,index.column()) ; + case Qt::BackgroundRole: return backgroundRole(fmpe,index.column()) ; + + case FilterRole: return filterRole (fmpe,index.column()) ; + case ThreadPinnedRole: return pinnedRole (fmpe,index.column()) ; + case MissingRole: return missingRole (fmpe,index.column()) ; + case StatusRole: return statusRole (fmpe,index.column()) ; + case SortRole: return sortRole (fmpe,index.column()) ; +#endif + default: + return QVariant(); + } +} + +#ifdef TODO +QVariant RsGxsForumModel::textColorRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if( (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING)) + return QVariant(mTextColorMissing); + + if(IS_MSG_UNREAD(fmpe.mMsgStatus) || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)) + return QVariant(mTextColorUnread); + else + return QVariant(mTextColorRead); + + return QVariant(); +} + +QVariant RsGxsForumModel::statusRole(const ForumModelPostEntry& fmpe,int column) const +{ + if(column != COLUMN_THREAD_DATA) + return QVariant(); + + return QVariant(fmpe.mMsgStatus); +} + +QVariant RsGxsForumModel::filterRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(!mFilteringEnabled || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER)) + return QVariant(FilterString); + + return QVariant(QString()); +} + +uint32_t RsGxsForumModel::recursUpdateFilterStatus(ForumModelIndex i,int column,const QStringList& strings) +{ + QString s ; + uint32_t count = 0; + + switch(column) + { + default: + case COLUMN_THREAD_DATE: + case COLUMN_THREAD_TITLE: s = displayRole(mPosts[i],column).toString(); + break; + case COLUMN_THREAD_AUTHOR: + { + QString comment ; + QList icons; + + GxsIdDetails::MakeIdDesc(mPosts[i].mAuthorId, false,s, icons, comment,GxsIdDetails::ICON_TYPE_NONE); + } + break; + } + + if(!strings.empty()) + { + mPosts[i].mPostFlags &= ~(ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER); + + for(auto iter(strings.begin()); iter != strings.end(); ++iter) + if(s.contains(*iter,Qt::CaseInsensitive)) + { + mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; + + count++; + break; + } + } + else + { + mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER |ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; + count++; + } + + for(uint32_t j=0;j 0) + mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; + } + + return count; +} + + +void RsGxsForumModel::setFilter(int column,const QStringList& strings,uint32_t& count) +{ + preMods(); + + if(!strings.empty()) + { + count = recursUpdateFilterStatus(ForumModelIndex(0),column,strings); + mFilteringEnabled = true; + } + else + { + count=0; + mFilteringEnabled = false; + } + + postMods(); +} + +QVariant RsGxsForumModel::missingRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) + return QVariant(true); + else + return QVariant(false); +} + +QVariant RsGxsForumModel::toolTipRole(const ForumModelPostEntry& fmpe,int column) const +{ + if(column == COLUMN_THREAD_DISTRIBUTION) + switch(fmpe.mReputationWarningLevel) + { + case 3: return QVariant(tr("Information for this identity is currently missing.")) ; + case 2: return QVariant(tr("You have banned this ID. The message will not be\ndisplayed nor forwarded to your friends.")) ; + case 1: return QVariant(tr("You have not set an opinion for this person,\n and your friends do not vote positively: Spam regulation \nprevents the message to be forwarded to your friends.")) ; + case 0: return QVariant(tr("Message will be forwarded to your friends.")) ; + default: + return QVariant("[ERROR: missing reputation level information - contact the developers]"); + } + + if(column == COLUMN_THREAD_AUTHOR) + { + QString str,comment ; + QList icons; + + if(!GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) + return QVariant(); + + int S = QFontMetricsF(QApplication::font()).height(); + QImage pix( (*icons.begin()).pixmap(QSize(4*S,4*S)).toImage()); + + QString embeddedImage; + if(RsHtml::makeEmbeddedImage(pix.scaled(QSize(4*S,4*S), Qt::KeepAspectRatio, Qt::SmoothTransformation), embeddedImage, 8*S * 8*S)) + comment = "
" + embeddedImage + "" + comment + "
"; + + return comment; + } + + return QVariant(); +} + +QVariant RsGxsForumModel::pinnedRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) + return QVariant(true); + else + return QVariant(false); +} + +QVariant RsGxsForumModel::backgroundRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) + return QVariant(QBrush(QColor(255,200,180))); + + if(mFilteringEnabled && (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_PASSES_FILTER)) + return QVariant(QBrush(QColor(255,240,210))); + + return QVariant(); +} +#endif + +QVariant RsGxsChannelPostsModel::sizeHintRole(int col) const +{ + float factor = QFontMetricsF(QApplication::font()).height()/14.0f ; + + return QVariant( QSize(factor * 170, factor*14 )); +#ifdef TODO + switch(col) + { + default: + case COLUMN_THREAD_TITLE: return QVariant( QSize(factor * 170, factor*14 )); + case COLUMN_THREAD_DATE: return QVariant( QSize(factor * 75 , factor*14 )); + case COLUMN_THREAD_AUTHOR: return QVariant( QSize(factor * 75 , factor*14 )); + case COLUMN_THREAD_DISTRIBUTION: return QVariant( QSize(factor * 15 , factor*14 )); + } +#endif +} + +#ifdef TODO +QVariant RsGxsForumModel::authorRole(const ForumModelPostEntry& fmpe,int column) const +{ + if(column == COLUMN_THREAD_DATA) + return QVariant(QString::fromStdString(fmpe.mAuthorId.toStdString())); + + return QVariant(); +} + +QVariant RsGxsForumModel::sortRole(const ForumModelPostEntry& fmpe,int column) const +{ + switch(column) + { + case COLUMN_THREAD_DATE: if(mSortMode == SORT_MODE_PUBLISH_TS) + return QVariant(QString::number(fmpe.mPublishTs)); // we should probably have leading zeroes here + else + return QVariant(QString::number(fmpe.mMostRecentTsInThread)); // we should probably have leading zeroes here + + case COLUMN_THREAD_READ: return QVariant((bool)IS_MSG_UNREAD(fmpe.mMsgStatus)); + case COLUMN_THREAD_DISTRIBUTION: return decorationRole(fmpe,column); + case COLUMN_THREAD_AUTHOR: + { + QString str,comment ; + QList icons; + GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, false, str, icons, comment,GxsIdDetails::ICON_TYPE_NONE); + + return QVariant(str); + } + default: + return displayRole(fmpe,column); + } +} +#endif + +QVariant RsGxsChannelPostsModel::displayRole(const RsGxsChannelPost& fmpe,int col) const +{ + switch(col) + { + default: + return QString::fromUtf8(fmpe.mMeta.mMsgName.c_str()); +#ifdef TODO + case COLUMN_THREAD_TITLE: if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_REDACTED) + return QVariant(tr("[ ... Redacted message ... ]")); + else if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) + return QVariant(tr("[PINNED] ") + QString::fromUtf8(fmpe.mTitle.c_str())); + else + return QVariant(QString::fromUtf8(fmpe.mTitle.c_str())); + + case COLUMN_THREAD_READ:return QVariant(); + case COLUMN_THREAD_DATE:{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) + return QVariant(QString()); + + QDateTime qtime; + qtime.setTime_t(fmpe.mPublishTs); + + return QVariant(DateTime::formatDateTime(qtime)); + } + + case COLUMN_THREAD_DISTRIBUTION: + case COLUMN_THREAD_AUTHOR:{ + QString name; + RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString()); + + if(id.isNull()) + return QVariant(tr("[Notification]")); + if(GxsIdTreeItemDelegate::computeName(id,name)) + return name; + return QVariant(tr("[Unknown]")); + } + case COLUMN_THREAD_MSGID: return QVariant(); +#endif +#ifdef TODO + if (filterColumn == COLUMN_THREAD_CONTENT) { + // need content for filter + QTextDocument doc; + doc.setHtml(QString::fromUtf8(msg.mMsg.c_str())); + item->setText(COLUMN_THREAD_CONTENT, doc.toPlainText().replace(QString("\n"), QString(" "))); + } +#endif + } + + + return QVariant("[ERROR]"); +} + +QVariant RsGxsChannelPostsModel::userRole(const RsGxsChannelPost& fmpe,int col) const +{ + switch(col) + { + default: + return QVariant::fromValue(fmpe); + } +} + +#ifdef TODO +QVariant RsGxsForumModel::decorationRole(const ForumModelPostEntry& fmpe,int col) const +{ + bool exist=false; + switch(col) + { + case COLUMN_THREAD_DISTRIBUTION: + return QVariant(fmpe.mReputationWarningLevel); + case COLUMN_THREAD_READ: + return QVariant(fmpe.mMsgStatus); + case COLUMN_THREAD_AUTHOR://Return icon as place holder. + return FilesDefs::getIconFromGxsIdCache(RsGxsId(fmpe.mAuthorId.toStdString()),QIcon(), exist); + } + return QVariant(); +} +#endif + +const RsGxsGroupId& RsGxsChannelPostsModel::currentGroupId() const +{ + return mChannelGroup.mMeta.mGroupId; +} + +void RsGxsChannelPostsModel::updateChannel(const RsGxsGroupId& channel_group_id) +{ + if(channel_group_id.isNull()) + return; + + update_posts(channel_group_id); +} + +void RsGxsChannelPostsModel::clear() +{ + preMods(); + + mPosts.clear(); + initEmptyHierarchy(mPosts); + + postMods(); + emit channelLoaded(); +} + +void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector& posts) +{ + preMods(); + + beginRemoveRows(QModelIndex(),0,mPosts.size()-1); + endRemoveRows(); + + mPosts.clear(); + mChannelGroup = group; + + createPostsArray(posts); + +#ifdef TODO + recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below) ; + recursUpdateFilterStatus(0,0,QStringList()); +#endif + +#ifdef DEBUG_CHANNEL_MODEL + // debug_dump(); +#endif + + beginInsertRows(QModelIndex(),0,mPosts.size()-1); + endInsertRows(); + + postMods(); + + emit channelLoaded(); +} + +void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id) +{ + if(group_id.isNull()) + return; + + RsThread::async([this, group_id]() + { + // 1 - get message data from p3GxsChannels + + std::list channelIds; + std::vector msg_metas; + std::vector groups; + + channelIds.push_back(group_id); + + if(!rsGxsChannels->getChannelsInfo(channelIds,groups) || groups.size() != 1) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel group info for channel " << group_id << std::endl; + return; + } + + RsGxsChannelGroup group = groups[0]; + + // We use the heap because the arrays need to be stored accross async + + std::vector *posts = new std::vector(); + std::vector *comments = new std::vector(); + std::vector *votes = new std::vector(); + + if(!rsGxsChannels->getChannelAllContent(group_id, *posts,*comments,*votes)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel messages for channel " << group_id << std::endl; + return; + } + + // 2 - update the model in the UI thread. + + RsQThreadUtils::postToObject( [group,posts,comments,votes,this]() + { + /* Here it goes any code you want to be executed on the Qt Gui + * thread, for example to update the data model with new information + * after a blocking call to RetroShare API complete, note that + * Qt::QueuedConnection is important! + */ + + setPosts(group,*posts) ; + + delete posts; + delete comments; + delete votes; + + }, this ); + + }); +} + +//ChannelPostsModelIndex RsGxsChannelPostsModel::addEntry(std::vector& posts,const ChannelPostsModelPostEntry& entry) +//{ +// uint32_t N = posts.size(); +// posts.push_back(entry); +// +//#ifdef DEBUG_FORUMMODEL +// std::cerr << "Added new entry " << N << " children of " << parent << std::endl; +//#endif +// if(N == parent) +// std::cerr << "(EE) trying to add a post as its own parent!" << std::endl; +// +// return ChannelPostsModelIndex(N); +//} + +//void RsGxsChannelPostsModel::convertMsgToPostEntry(const RsGxsChannelGroup& mChannelGroup,const RsMsgMetaData& msg, bool /*useChildTS*/, ChannelPostsModelPostEntry& fentry) +//{ +// fentry.mTitle = msg.mMsgName; +// fentry.mMsgId = msg.mMsgId; +// fentry.mPublishTs = msg.mPublishTs; +// fentry.mPostFlags = 0; +// fentry.mMsgStatus = msg.mMsgStatus; +// +// // Early check for a message that should be hidden because its author +// // is flagged with a bad reputation +//} + +static bool decreasing_time_comp(const std::pair& e1,const std::pair& e2) { return e2.first < e1.first ; } + +void RsGxsChannelPostsModel::createPostsArray(std::vector& posts) +{ + // collect new versions of posts if any + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Inserting channel posts" << std::endl; +#endif + + std::vector new_versions ; + for (uint32_t i=0;i search_map ; + for (uint32_t i=0;i versions ; + std::map::const_iterator vit ; + + while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId))) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ; +#endif + + // Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple + // times. In this case, we only need to replace the post with the newest version + + //uint32_t prev_index = current_index ; + current_index = vit->second ; + + if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further. + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " already erased. Stopping." << std::endl; +#endif + break ; + } + + if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " and is more recent => following" << std::endl; +#endif + for(std::set::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt) + posts[source_index].mOlderVersions.insert(*itt); + + posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId); + posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored + } +#ifdef DEBUG_CHANNEL_MODEL + else + std::cerr << " but is older -> Stopping" << std::endl; +#endif + } + } + } + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Now adding " << posts.size() << " posts into array structure..." << std::endl; +#endif + + mPosts.clear(); + + for (std::vector::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " adding post: " << (*it).mMeta.mMsgId ; +#endif + + if(!(*it).mMeta.mMsgId.isNull()) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " added" << std::endl; +#endif + mPosts.push_back(*it); + } +#ifdef DEBUG_CHANNEL_MODEL + else + std::cerr << " skipped" << std::endl; +#endif + } +} + +void RsGxsChannelPostsModel::setMsgReadStatus(const QModelIndex& i,bool read_status,bool with_children) +{ + if(!i.isValid()) + return ; + + // no need to call preMods()/postMods() here because we'renot changing the model + + quintptr ref = i.internalId(); + uint32_t entry = 0; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) + return ; + +#warning TODO +// bool has_unread_below, has_read_below; +// recursSetMsgReadStatus(entry,read_status,with_children) ; +// recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below); + +} + +#ifdef TODO +void RsGxsForumModel::recursSetMsgReadStatus(ForumModelIndex i,bool read_status,bool with_children) +{ + int newStatus = (read_status ? mPosts[i].mMsgStatus & ~static_cast(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD) + : mPosts[i].mMsgStatus | static_cast(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD)); + bool bChanged = (mPosts[i].mMsgStatus != newStatus); + mPosts[i].mMsgStatus = newStatus; + //Remove Unprocessed and New flags + mPosts[i].mMsgStatus &= ~(GXS_SERV::GXS_MSG_STATUS_UNPROCESSED | GXS_SERV::GXS_MSG_STATUS_GUI_NEW); + + if (bChanged) + { + //Don't recurs post versions as this should be done before, if no change. + uint32_t token; + auto s = getPostVersions(mPosts[i].mMsgId) ; + + if(!s.empty()) + for(auto it(s.begin());it!=s.end();++it) + { + rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, it->second ), read_status); + std::cerr << "Setting version " << it->second << " of post " << mPosts[i].mMsgId << " as read." << std::endl; + } + else + rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, mPosts[i].mMsgId ), read_status); + + QModelIndex itemIndex = createIndex(i - 1, 0, &mPosts[i]); + emit dataChanged(itemIndex, itemIndex); + } + + if(!with_children) + return; + + for(uint32_t j=0;j& entries,ForumModelIndex index,int depth) +{ + const ForumModelPostEntry& e(entries[index]); + + QDateTime qtime; + qtime.setTime_t(e.mPublishTs); + + std::cerr << std::string(depth*2,' ') << index << " : " << e.mAuthorId.toStdString() << " " + << QString("%1").arg((uint32_t)e.mPostFlags,8,16,QChar('0')).toStdString() << " " + << QString("%1").arg((uint32_t)e.mMsgStatus,8,16,QChar('0')).toStdString() << " " + << qtime.toString().toStdString() << " \"" << e.mTitle << "\"" << std::endl; + + for(uint32_t i=0;i= mPosts.size()) + return ; + + std::cerr << "Setting own opinion for author " << mPosts[entry].mAuthorId + << " to " << static_cast(op) << std::endl; + RsGxsId author_id = mPosts[entry].mAuthorId; + + rsReputations->setOwnOpinion(author_id,op) ; + + // update opinions and distribution flags. No need to re-load all posts. + + for(uint32_t i=0;i * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsgxsifacetypes.h" +#include +#include + +// This class holds the actual hierarchy of posts, represented by identifiers +// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to +// safely access the data. + +// The model contains a post in place 0 that is the parent of all posts. + +typedef uint32_t ChannelPostsModelIndex; + +// struct ChannelPostsModelPostEntry +// { +// ChannelPostsModelPostEntry() : mPublishTs(0),mPostFlags(0),mMsgStatus(0),prow(0) {} +// +// enum { // flags for display of posts. To be used in mPostFlags +// FLAG_POST_IS_PINNED = 0x0001, +// FLAG_POST_IS_MISSING = 0x0002, +// FLAG_POST_IS_REDACTED = 0x0004, +// FLAG_POST_HAS_UNREAD_CHILDREN = 0x0008, +// FLAG_POST_HAS_READ_CHILDREN = 0x0010, +// FLAG_POST_PASSES_FILTER = 0x0020, +// FLAG_POST_CHILDREN_PASSES_FILTER = 0x0040, +// }; +// +// std::string mTitle ; +// RsGxsMessageId mMsgId; +// uint32_t mPublishTs; +// uint32_t mPostFlags; +// int mMsgStatus; +// +// int prow ;// parent row, which basically means position in the array of posts +// }; + +// This class is the item model used by Qt to display the information + +class RsGxsChannelPostsModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit RsGxsChannelPostsModel(QObject *parent = NULL); + ~RsGxsChannelPostsModel(){} + + static const int COLUMN_THREAD_NB_COLUMNS = 0x01; + +#ifdef TODO + enum Columns { + COLUMN_THREAD_TITLE =0x00, + COLUMN_THREAD_READ =0x01, + COLUMN_THREAD_DATE =0x02, + COLUMN_THREAD_DISTRIBUTION =0x03, + COLUMN_THREAD_AUTHOR =0x04, + COLUMN_THREAD_CONTENT =0x05, + COLUMN_THREAD_MSGID =0x06, + COLUMN_THREAD_DATA =0x07, + }; + + enum Roles{ SortRole = Qt::UserRole+1, + ThreadPinnedRole = Qt::UserRole+2, + MissingRole = Qt::UserRole+3, + StatusRole = Qt::UserRole+4, + UnreadChildrenRole = Qt::UserRole+5, + FilterRole = Qt::UserRole+6, + }; +#endif + + enum TreeMode{ TREE_MODE_UNKWN = 0x00, + TREE_MODE_PLAIN = 0x01, + TREE_MODE_FILES = 0x02, + }; + +#ifdef TODO + enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00, + SORT_MODE_CHILDREN_PUBLISH_TS = 0x01, + }; +#endif + + QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} + QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const; + + static const QString FilterString ; + + std::vector > getPostVersions(const RsGxsMessageId& mid) const; + + // This method will asynchroneously update the data + void updateChannel(const RsGxsGroupId& channel_group_id); + const RsGxsGroupId& currentGroupId() const; + + void setTreeMode(TreeMode mode) ; +#ifdef TODO + void setSortMode(SortMode mode) ; + + void setTextColorRead (QColor color) { mTextColorRead = color;} + void setTextColorUnread (QColor color) { mTextColorUnread = color;} + void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} + void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} + void setTextColorMissing (QColor color) { mTextColorMissing = color;} +#endif + + void setMsgReadStatus(const QModelIndex &i, bool read_status, bool with_children); +#ifdef TODO + void setFilter(int column, const QStringList &strings, uint32_t &count) ; + void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); +#endif + + // Helper functions + + bool getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const ; + void clear() ; + + // AbstractItemModel functions. + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& child) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Custom item roles + + QVariant sizeHintRole (int col) const; + QVariant displayRole (const RsGxsChannelPost& fmpe, int col) const; + QVariant toolTipRole (const RsGxsChannelPost& fmpe, int col) const; + QVariant userRole (const RsGxsChannelPost& fmpe, int col) const; +#ifdef TODO + QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; + QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant sortRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; +#endif + + /*! + * \brief debug_dump + * Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct. + */ + void debug_dump(); + +signals: + void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI. + +private: + RsGxsChannelGroup mChannelGroup; + +#ifdef TODO + bool mUseChildTS; + bool mFilteringEnabled; + SortMode mSortMode; +#endif + TreeMode mTreeMode; + + uint32_t mColumns; + + void preMods() ; + void postMods() ; + + quintptr getParentRow(quintptr ref,int& row) const; + quintptr getChildRef(quintptr ref, int index) const; + int getChildrenCount(quintptr ref) const; + + static bool convertTabEntryToRefPointer(uint32_t entry, quintptr &ref); + static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry); + static void computeReputationLevel(uint32_t forum_sign_flags, RsGxsChannelPost& entry); + + void update_posts(const RsGxsGroupId& group_id); + +#ifdef TODO + void setForumMessageSummary(const std::vector& messages); +#endif + void recursUpdateReadStatusAndTimes(ChannelPostsModelIndex i,bool& has_unread_below,bool& has_read_below); + uint32_t recursUpdateFilterStatus(ChannelPostsModelIndex i,int column,const QStringList& strings); + void recursSetMsgReadStatus(ChannelPostsModelIndex i,bool read_status,bool with_children); + +#ifdef TODO + static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); +#endif + //static ChannelModelIndex addEntry(std::vector& posts,const ChannelModelPostEntry& entry,ChannelModelIndex parent); + //static void convertMsgToPostEntry(const RsGxsChannelGroup &mChannelGroup, const RsMsgMetaData &msg, bool useChildTS, ChannelModelPostEntry& fentry); + + //void computeMessagesHierarchy(const RsGxsChannelGroup& forum_group, const std::vector &msgs_array, std::vector &posts, std::map > > &mPostVersions); + void createPostsArray(std::vector &posts); + void setPosts(const RsGxsChannelGroup& group, std::vector &posts); + void initEmptyHierarchy(std::vector& posts); + + std::vector mPosts ; // store the list of posts updated from rsForums. + //std::map > > mPostVersions; // stores versions of posts + + QColor mTextColorRead ; + QColor mTextColorUnread ; + QColor mTextColorUnreadChildren; + QColor mTextColorNotSubscribed ; + QColor mTextColorMissing ; + + friend class const_iterator; +}; From e0cf9768fce3c21936a619d6c57befc441cc4c0d Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 4 Jun 2020 19:50:34 +0200 Subject: [PATCH 05/89] improved layout --- .../GxsChannelPostsWidgetWithModel.cpp | 168 ++++---- .../GxsChannelPostsWidgetWithModel.ui | 401 +++++++++--------- 2 files changed, 295 insertions(+), 274 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index ad695136d..0bd08b9d0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -49,6 +49,10 @@ * #define DEBUG_CHANNEL ***/ +static const int mTokenTypeGroupData = 1; +static const int CHANNEL_TABS_DETAILS= 0; +static const int CHANNEL_TABS_POSTS = 1; + /* View mode */ #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 @@ -255,9 +259,18 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrpostsTree->selectionModel()->currentIndex(); + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); - std::cerr << "Showing details about selected index : "<< selected_index.row() << "," << selected_index.column() << std::endl; + if(!index.isValid()) + { + ui->postDetails_TE->clear(); + return; + } + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl; + + ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS)); } void GxsChannelPostsWidgetWithModel::updateGroupData() @@ -285,6 +298,7 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() { mGroup = groups[0]; mThreadModel->updateChannel(groupId()); + insertChannelDetails(mGroup); } ); }); } @@ -427,105 +441,99 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou } else { chanImage = QPixmap(CHAN_DEFAULT_IMAGE); } - //ui->logoLabel->setPixmap(chanImage); + ui->logoLabel->setPixmap(chanImage); + ui->logoLabel->setFixedSize(QSize(ui->logoLabel->height()*chanImage.width()/(float)chanImage.height(),ui->logoLabel->height())); // make the logo have the same aspect ratio than the original image - if (group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH) - { - mStateHelper->setWidgetEnabled(ui->postButton, true); - } - else - { - mStateHelper->setWidgetEnabled(ui->postButton, false); - } + ui->postButton->setEnabled(bool(group.mMeta.mSubscribeFlags & GXS_SERV::GROUP_SUBSCRIBE_PUBLISH)); ui->subscribeToolButton->setSubscribed(IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)); - mStateHelper->setWidgetEnabled(ui->subscribeToolButton, true); + ui->subscribeToolButton->setEnabled(true); - bool autoDownload ; - rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); + bool autoDownload ; + rsGxsChannels->getChannelAutoDownload(group.mMeta.mGroupId,autoDownload); setAutoDownload(autoDownload); - + RetroShareLink link; - if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) { - ui->feedToolButton->setEnabled(true); - - ui->fileToolButton->setEnabled(true); - //ui->infoWidget->hide(); - setViewMode(viewMode()); - + if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) + { ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) ); + ui->feedToolButton->setEnabled(true); + ui->fileToolButton->setEnabled(true); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_POSTS,true); + ui->details_TW->setEnabled(true); + } + else + { + ui->details_TW->setEnabled(false); + ui->channel_TW->setTabEnabled(CHANNEL_TABS_POSTS,false); + } - ui->infoPosts->clear(); - ui->infoDescription->clear(); - } else { - ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); - if(group.mMeta.mLastPost==0) - ui->infoLastPost->setText(tr("Never")); - else - ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); - QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); + ui->infoPosts->setText(QString::number(group.mMeta.mVisibleMsgCount)); + if(group.mMeta.mLastPost==0) + ui->infoLastPost->setText(tr("Never")); + else + ui->infoLastPost->setText(DateTime::formatLongDateTime(group.mMeta.mLastPost)); + QString formatDescription = QString::fromUtf8(group.mDescription.c_str()); - unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; + unsigned int formatFlag = RSHTML_FORMATTEXT_EMBED_LINKS; - // embed smileys ? - if (Settings->valueFromGroup(QString("ChannelPostsWidget"), QString::fromUtf8("Emoteicons_ChannelDecription"), true).toBool()) { - formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS; - } + // embed smileys ? + if (Settings->valueFromGroup(QString("ChannelPostsWidget"), QString::fromUtf8("Emoteicons_ChannelDecription"), true).toBool()) { + formatFlag |= RSHTML_FORMATTEXT_EMBED_SMILEYS; + } - formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); + formatDescription = RsHtml().formatText(NULL, formatDescription, formatFlag); - ui->infoDescription->setText(formatDescription); - - ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; - - link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); - ui->infoAdministrator->setText(link.toHtml()); - - ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); + ui->infoDescription->setText(formatDescription); - QString distrib_string ( "[unknown]" ); - - switch(group.mMeta.mCircleType) - { - case GXS_CIRCLE_TYPE_PUBLIC: distrib_string = tr("Public") ; - break ; - case GXS_CIRCLE_TYPE_EXTERNAL: - { - RsGxsCircleDetails det ; + ui->infoAdministrator->setId(group.mMeta.mAuthorId) ; - // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. + link = RetroShareLink::createMessage(group.mMeta.mAuthorId, ""); + ui->infoAdministrator->setText(link.toHtml()); - if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) - distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; - else - distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; - } - break ; - case GXS_CIRCLE_TYPE_YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); - break ; - case GXS_CIRCLE_TYPE_LOCAL: distrib_string = tr("You and your friend nodes"); - break ; - default: - std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; - } - - ui->infoDistribution->setText(distrib_string); + ui->infoCreated->setText(DateTime::formatLongDateTime(group.mMeta.mPublishTs)); + + QString distrib_string ( "[unknown]" ); + + switch(group.mMeta.mCircleType) + { + case GXS_CIRCLE_TYPE_PUBLIC: distrib_string = tr("Public") ; + break ; + case GXS_CIRCLE_TYPE_EXTERNAL: + { + RsGxsCircleDetails det ; + + // !! What we need here is some sort of CircleLabel, which loads the circle and updates the label when done. + + if(rsGxsCircles->getCircleDetails(group.mMeta.mCircleId,det)) + distrib_string = tr("Restricted to members of circle \"")+QString::fromUtf8(det.mCircleName.c_str()) +"\""; + else + distrib_string = tr("Restricted to members of circle ")+QString::fromStdString(group.mMeta.mCircleId.toStdString()) ; + } + break ; + case GXS_CIRCLE_TYPE_YOUR_EYES_ONLY: distrib_string = tr("Your eyes only"); + break ; + case GXS_CIRCLE_TYPE_LOCAL: distrib_string = tr("You and your friend nodes"); + break ; + default: + std::cerr << "(EE) badly initialised group distribution ID = " << group.mMeta.mCircleType << std::endl; + } + + ui->infoDistribution->setText(distrib_string); #ifdef TODO - ui->infoWidget->show(); - ui->feedWidget->hide(); - ui->fileWidget->hide(); + ui->infoWidget->show(); + ui->feedWidget->hide(); + ui->fileWidget->hide(); #endif - ui->feedToolButton->setEnabled(false); - ui->fileToolButton->setEnabled(false); - - ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); + ui->feedToolButton->setEnabled(false); + ui->fileToolButton->setEnabled(false); - } + ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); } int GxsChannelPostsWidgetWithModel::viewMode() @@ -870,8 +878,8 @@ void GxsChannelPostsWidget::clearPosts() void GxsChannelPostsWidgetWithModel::blank() { - //mStateHelper->setWidgetEnabled(ui->postButton, false); - //mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); + mStateHelper->setWidgetEnabled(ui->postButton, false); + mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); mThreadModel->clear(); groupNameChanged(QString()); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 2ae7ecac8..5cea1d692 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -222,182 +222,182 @@ - + - 0 + 1 Channel details - + - - - Channel details - - - - 9 - - - 9 - - - 9 - - - 9 - - - - - - 0 - 0 - - - - - 75 - true - - - - Last Post: - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> + + + + + TextLabel + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + + + unknown + + + true + + + + + + + unknown + + + + + + + + 75 + true + + + + Created: + + + + + + + unknown + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Posts: + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + 0 + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: + + + + + + + unknown + + + + + + + + 75 + true + + + + Administrator: + + + + + + + + 75 + true + + + + Distribution: + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } </style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html> - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - true - - - true - - - - - - - - 75 - true - - - - Description: - - - - - - - - 75 - true - - - - Created: - - - - - - - - 75 - true - - - - Administrator: - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Posts: - - - - - - - - 75 - true - - - - Distribution: - - - - - - - unknown - - - - - - - unknown - - - - - - - unknown - - - true - - - - - - - unknown - - - - - - - 0 - - - - + + + Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse + + + true + + + true + @@ -406,43 +406,56 @@ p, li { white-space: pre-wrap; } Posts - + - - - QAbstractItemView::SelectItems + + + Qt::Vertical - - false - + + + QAbstractItemView::SelectItems + + + false + + + + + 2 + + + + Details + + + + + 9 + 9 + 955 + 181 + + + + + + + Files + + + + + Comments + + + - - - - 0 - - - - Details - - - - - - - - - - Comments - - - - From a5dd33e085be81063303105d323d3caed8970a33 Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 4 Jun 2020 21:50:27 +0200 Subject: [PATCH 06/89] improved widgets. Added file list (with dump delegate), labels, etc --- .../gxschannels/GxsChannelPostFilesModel.cpp | 799 ++++++++++++++++++ .../gxschannels/GxsChannelPostFilesModel.h | 161 ++++ .../GxsChannelPostsWidgetWithModel.cpp | 102 ++- .../GxsChannelPostsWidgetWithModel.h | 18 +- .../GxsChannelPostsWidgetWithModel.ui | 66 +- retroshare-gui/src/retroshare-gui.pro | 2 + 6 files changed, 1129 insertions(+), 19 deletions(-) create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp new file mode 100644 index 000000000..9098af57b --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -0,0 +1,799 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp * + * * + * Copyright 2020 by Cyril Soler * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#include +#include +#include +#include + +#include "gui/common/FilesDefs.h" +#include "util/qtthreadsutils.h" +#include "util/HandleRichText.h" +#include "util/DateTime.h" +#include "retroshare/rsgxsflags.h" +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsexpr.h" + +#include "GxsChannelPostFilesModel.h" + +#define DEBUG_CHANNEL_MODEL + +Q_DECLARE_METATYPE(RsGxsFile) + +static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere + +RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent) + : QAbstractItemModel(parent) +{ + initEmptyHierarchy(mFiles); +} + +void RsGxsChannelPostFilesModel::initEmptyHierarchy(std::vector& files) +{ + preMods(); + + files.resize(1); // adds a sentinel item + files[0].mName = "Root sentinel post" ; + + postMods(); +} + +void RsGxsChannelPostFilesModel::preMods() +{ + //emit layoutAboutToBeChanged(); //Generate SIGSEGV when click on button move next/prev. + + beginResetModel(); +} +void RsGxsChannelPostFilesModel::postMods() +{ + endResetModel(); + + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL)); +} + +#ifdef TODO +void RsGxsChannelPostsModel::setSortMode(SortMode mode) +{ + preMods(); + + mSortMode = mode; + + postMods(); +} + +void RsGxsForumModel::initEmptyHierarchy(std::vector& posts) +{ + preMods(); + + posts.resize(1); // adds a sentinel item + posts[0].mTitle = "Root sentinel post" ; + posts[0].mParent = 0; + + postMods(); +} +#endif + +int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const +{ + if(parent.column() > 0) + return 0; + + if(mFiles.empty()) // security. Should never happen. + return 0; + + if(!parent.isValid()) + return getChildrenCount(0); + + RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; + return 0; +} + +int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const +{ + return COLUMN_THREAD_NB_COLUMNS ; +} + +// std::vector > RsGxsChannelPostsModel::getPostVersions(const RsGxsMessageId& mid) const +// { +// auto it = mPostVersions.find(mid); +// +// if(it != mPostVersions.end()) +// return it->second; +// else +// return std::vector >(); +// } + +bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,RsGxsFile& fmpe) const +{ + if(!i.isValid()) + return true; + + quintptr ref = i.internalId(); + uint32_t entry = 0; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) + return false ; + + fmpe = mFiles[entry]; + + return true; + +} + +bool RsGxsChannelPostFilesModel::hasChildren(const QModelIndex &parent) const +{ + if(!parent.isValid()) + return true; + + return false; // by default, no channel post has children +} + +bool RsGxsChannelPostFilesModel::convertTabEntryToRefPointer(uint32_t entry,quintptr& ref) +{ + // the pointer is formed the following way: + // + // [ 32 bits ] + // + // This means that the whole software has the following build-in limitation: + // * 4 B simultaenous posts. Should be enough ! + + ref = (intptr_t)entry; + + return true; +} + +bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& entry) +{ + intptr_t val = (intptr_t)ref; + + if(val > (1<<30)) // make sure the pointer is an int that fits in 32bits and not too big which would look suspicious + { + RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl; + return false ; + } + entry = quintptr(val); + + return true; +} + +QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const +{ + if(row < 0 || column < 0 || column >= COLUMN_THREAD_NB_COLUMNS) + return QModelIndex(); + + quintptr ref = getChildRef(parent.internalId(),row); + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl; +#endif + return createIndex(row,column,ref) ; +} + +QModelIndex RsGxsChannelPostFilesModel::parent(const QModelIndex& index) const +{ + if(!index.isValid()) + return QModelIndex(); + + return QModelIndex(); // there's no hierarchy here. So nothing to do! +} + +Qt::ItemFlags RsGxsChannelPostFilesModel::flags(const QModelIndex& index) const +{ + if (!index.isValid()) + return 0; + + return QAbstractItemModel::flags(index); +} + +quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const +{ + if (index < 0) + return 0; + + ChannelPostFilesModelIndex entry ; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) + return 0 ; + + if(entry == 0) + { + quintptr new_ref; + convertTabEntryToRefPointer(index+1,new_ref); + return new_ref; + } + else + return 0 ; +} + +quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const +{ + ChannelPostFilesModelIndex ref_entry; + + if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFiles.size()) + return 0 ; + + if(ref_entry == 0) + { + RsErr() << "getParentRow() shouldn't be asked for the parent of NULL" << std::endl; + row = 0; + } + else + row = ref_entry-1; + + return 0; +} + +int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const +{ + uint32_t entry = 0 ; + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) + return 0 ; + + if(entry == 0) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Children count (flat mode): " << mFiles.size()-1 << std::endl; +#endif + return ((int)mFiles.size())-1; + } + else + return 0; +} + +QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const +{ +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "calling data(" << index << ") role=" << role << std::endl; +#endif + + if(!index.isValid()) + return QVariant(); + + switch(role) + { + case Qt::SizeHintRole: return sizeHintRole(index.column()) ; + case Qt::StatusTipRole:return QVariant(); + default: break; + } + + quintptr ref = (index.isValid())?index.internalId():0 ; + uint32_t entry = 0; + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "data(" << index << ")" ; +#endif + + if(!ref) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " [empty]" << std::endl; +#endif + return QVariant() ; + } + + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) + { +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << "Bad pointer: " << (void*)ref << std::endl; +#endif + return QVariant() ; + } + + const RsGxsFile& fmpe(mFiles[entry]); + +#ifdef TODO + if(role == Qt::FontRole) + { + QFont font ; + font.setBold( (fmpe.mPostFlags & (ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN | ForumModelPostEntry::FLAG_POST_IS_PINNED)) || IS_MSG_UNREAD(fmpe.mMsgStatus)); + return QVariant(font); + } + + if(role == UnreadChildrenRole) + return bool(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN); + +#ifdef DEBUG_CHANNEL_MODEL + std::cerr << " [ok]" << std::endl; +#endif +#endif + + switch(role) + { + case Qt::DisplayRole: return displayRole (fmpe,index.column()) ; + case Qt::UserRole: return userRole (fmpe,index.column()) ; +#ifdef TODO + case Qt::DecorationRole: return decorationRole(fmpe,index.column()) ; + case Qt::ToolTipRole: return toolTipRole (fmpe,index.column()) ; + case Qt::TextColorRole: return textColorRole (fmpe,index.column()) ; + case Qt::BackgroundRole: return backgroundRole(fmpe,index.column()) ; + + case FilterRole: return filterRole (fmpe,index.column()) ; + case ThreadPinnedRole: return pinnedRole (fmpe,index.column()) ; + case MissingRole: return missingRole (fmpe,index.column()) ; + case StatusRole: return statusRole (fmpe,index.column()) ; + case SortRole: return sortRole (fmpe,index.column()) ; +#endif + default: + return QVariant(); + } +} + +#ifdef TODO +QVariant RsGxsForumModel::textColorRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if( (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING)) + return QVariant(mTextColorMissing); + + if(IS_MSG_UNREAD(fmpe.mMsgStatus) || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)) + return QVariant(mTextColorUnread); + else + return QVariant(mTextColorRead); + + return QVariant(); +} + +QVariant RsGxsForumModel::statusRole(const ForumModelPostEntry& fmpe,int column) const +{ + if(column != COLUMN_THREAD_DATA) + return QVariant(); + + return QVariant(fmpe.mMsgStatus); +} + +QVariant RsGxsForumModel::filterRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(!mFilteringEnabled || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER)) + return QVariant(FilterString); + + return QVariant(QString()); +} + +uint32_t RsGxsForumModel::recursUpdateFilterStatus(ForumModelIndex i,int column,const QStringList& strings) +{ + QString s ; + uint32_t count = 0; + + switch(column) + { + default: + case COLUMN_THREAD_DATE: + case COLUMN_THREAD_TITLE: s = displayRole(mPosts[i],column).toString(); + break; + case COLUMN_THREAD_AUTHOR: + { + QString comment ; + QList icons; + + GxsIdDetails::MakeIdDesc(mPosts[i].mAuthorId, false,s, icons, comment,GxsIdDetails::ICON_TYPE_NONE); + } + break; + } + + if(!strings.empty()) + { + mPosts[i].mPostFlags &= ~(ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER); + + for(auto iter(strings.begin()); iter != strings.end(); ++iter) + if(s.contains(*iter,Qt::CaseInsensitive)) + { + mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; + + count++; + break; + } + } + else + { + mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER |ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; + count++; + } + + for(uint32_t j=0;j 0) + mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; + } + + return count; +} + + +void RsGxsForumModel::setFilter(int column,const QStringList& strings,uint32_t& count) +{ + preMods(); + + if(!strings.empty()) + { + count = recursUpdateFilterStatus(ForumModelIndex(0),column,strings); + mFilteringEnabled = true; + } + else + { + count=0; + mFilteringEnabled = false; + } + + postMods(); +} + +QVariant RsGxsForumModel::missingRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) + return QVariant(true); + else + return QVariant(false); +} + +QVariant RsGxsForumModel::toolTipRole(const ForumModelPostEntry& fmpe,int column) const +{ + if(column == COLUMN_THREAD_DISTRIBUTION) + switch(fmpe.mReputationWarningLevel) + { + case 3: return QVariant(tr("Information for this identity is currently missing.")) ; + case 2: return QVariant(tr("You have banned this ID. The message will not be\ndisplayed nor forwarded to your friends.")) ; + case 1: return QVariant(tr("You have not set an opinion for this person,\n and your friends do not vote positively: Spam regulation \nprevents the message to be forwarded to your friends.")) ; + case 0: return QVariant(tr("Message will be forwarded to your friends.")) ; + default: + return QVariant("[ERROR: missing reputation level information - contact the developers]"); + } + + if(column == COLUMN_THREAD_AUTHOR) + { + QString str,comment ; + QList icons; + + if(!GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) + return QVariant(); + + int S = QFontMetricsF(QApplication::font()).height(); + QImage pix( (*icons.begin()).pixmap(QSize(4*S,4*S)).toImage()); + + QString embeddedImage; + if(RsHtml::makeEmbeddedImage(pix.scaled(QSize(4*S,4*S), Qt::KeepAspectRatio, Qt::SmoothTransformation), embeddedImage, 8*S * 8*S)) + comment = "
" + embeddedImage + "" + comment + "
"; + + return comment; + } + + return QVariant(); +} + +QVariant RsGxsForumModel::pinnedRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) + return QVariant(true); + else + return QVariant(false); +} + +QVariant RsGxsForumModel::backgroundRole(const ForumModelPostEntry& fmpe,int /*column*/) const +{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) + return QVariant(QBrush(QColor(255,200,180))); + + if(mFilteringEnabled && (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_PASSES_FILTER)) + return QVariant(QBrush(QColor(255,240,210))); + + return QVariant(); +} +#endif + +QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const +{ + float factor = QFontMetricsF(QApplication::font()).height()/14.0f ; + + return QVariant( QSize(factor * 170, factor*14 )); +#ifdef TODO + switch(col) + { + default: + case COLUMN_THREAD_TITLE: return QVariant( QSize(factor * 170, factor*14 )); + case COLUMN_THREAD_DATE: return QVariant( QSize(factor * 75 , factor*14 )); + case COLUMN_THREAD_AUTHOR: return QVariant( QSize(factor * 75 , factor*14 )); + case COLUMN_THREAD_DISTRIBUTION: return QVariant( QSize(factor * 15 , factor*14 )); + } +#endif +} + +#ifdef TODO +QVariant RsGxsForumModel::authorRole(const ForumModelPostEntry& fmpe,int column) const +{ + if(column == COLUMN_THREAD_DATA) + return QVariant(QString::fromStdString(fmpe.mAuthorId.toStdString())); + + return QVariant(); +} + +QVariant RsGxsForumModel::sortRole(const ForumModelPostEntry& fmpe,int column) const +{ + switch(column) + { + case COLUMN_THREAD_DATE: if(mSortMode == SORT_MODE_PUBLISH_TS) + return QVariant(QString::number(fmpe.mPublishTs)); // we should probably have leading zeroes here + else + return QVariant(QString::number(fmpe.mMostRecentTsInThread)); // we should probably have leading zeroes here + + case COLUMN_THREAD_READ: return QVariant((bool)IS_MSG_UNREAD(fmpe.mMsgStatus)); + case COLUMN_THREAD_DISTRIBUTION: return decorationRole(fmpe,column); + case COLUMN_THREAD_AUTHOR: + { + QString str,comment ; + QList icons; + GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, false, str, icons, comment,GxsIdDetails::ICON_TYPE_NONE); + + return QVariant(str); + } + default: + return displayRole(fmpe,column); + } +} +#endif + +QVariant RsGxsChannelPostFilesModel::displayRole(const RsGxsFile& fmpe,int col) const +{ + switch(col) + { + case 0: return QString::fromUtf8(fmpe.mName.c_str()); + case 1: return QString::number(fmpe.mSize); + case 2: { + FileInfo finfo; + if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) + return qulonglong(finfo.transfered); + else + return 0; + } + default: + return QString(); +#ifdef TODO + case COLUMN_THREAD_TITLE: if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_REDACTED) + return QVariant(tr("[ ... Redacted message ... ]")); + else if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) + return QVariant(tr("[PINNED] ") + QString::fromUtf8(fmpe.mTitle.c_str())); + else + return QVariant(QString::fromUtf8(fmpe.mTitle.c_str())); + + case COLUMN_THREAD_READ:return QVariant(); + case COLUMN_THREAD_DATE:{ + if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) + return QVariant(QString()); + + QDateTime qtime; + qtime.setTime_t(fmpe.mPublishTs); + + return QVariant(DateTime::formatDateTime(qtime)); + } + + case COLUMN_THREAD_DISTRIBUTION: + case COLUMN_THREAD_AUTHOR:{ + QString name; + RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString()); + + if(id.isNull()) + return QVariant(tr("[Notification]")); + if(GxsIdTreeItemDelegate::computeName(id,name)) + return name; + return QVariant(tr("[Unknown]")); + } + case COLUMN_THREAD_MSGID: return QVariant(); +#endif +#ifdef TODO + if (filterColumn == COLUMN_THREAD_CONTENT) { + // need content for filter + QTextDocument doc; + doc.setHtml(QString::fromUtf8(msg.mMsg.c_str())); + item->setText(COLUMN_THREAD_CONTENT, doc.toPlainText().replace(QString("\n"), QString(" "))); + } +#endif + } + + + return QVariant("[ERROR]"); +} + +QVariant RsGxsChannelPostFilesModel::userRole(const RsGxsFile& fmpe,int col) const +{ + switch(col) + { + default: + return QVariant::fromValue(fmpe); + } +} + +#ifdef TODO +QVariant RsGxsForumModel::decorationRole(const ForumModelPostEntry& fmpe,int col) const +{ + bool exist=false; + switch(col) + { + case COLUMN_THREAD_DISTRIBUTION: + return QVariant(fmpe.mReputationWarningLevel); + case COLUMN_THREAD_READ: + return QVariant(fmpe.mMsgStatus); + case COLUMN_THREAD_AUTHOR://Return icon as place holder. + return FilesDefs::getIconFromGxsIdCache(RsGxsId(fmpe.mAuthorId.toStdString()),QIcon(), exist); + } + return QVariant(); +} +#endif + +void RsGxsChannelPostFilesModel::clear() +{ + preMods(); + + mFiles.clear(); + initEmptyHierarchy(mFiles); + + postMods(); + emit channelLoaded(); +} + +void RsGxsChannelPostFilesModel::setFiles(const std::list& files) +{ + preMods(); + + beginRemoveRows(QModelIndex(),0,mFiles.size()-1); + endRemoveRows(); + + mFiles.clear(); + initEmptyHierarchy(mFiles); + + for(auto& file:files) + mFiles.push_back(file); + +#ifdef TODO + recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below) ; + recursUpdateFilterStatus(0,0,QStringList()); +#endif + +#ifdef DEBUG_CHANNEL_MODEL + // debug_dump(); +#endif + + beginInsertRows(QModelIndex(),0,mFiles.size()-1); + endInsertRows(); + + postMods(); + + emit channelLoaded(); +} + +//ChannelPostsModelIndex RsGxsChannelPostsModel::addEntry(std::vector& posts,const ChannelPostsModelPostEntry& entry) +//{ +// uint32_t N = posts.size(); +// posts.push_back(entry); +// +//#ifdef DEBUG_FORUMMODEL +// std::cerr << "Added new entry " << N << " children of " << parent << std::endl; +//#endif +// if(N == parent) +// std::cerr << "(EE) trying to add a post as its own parent!" << std::endl; +// +// return ChannelPostsModelIndex(N); +//} + +//void RsGxsChannelPostsModel::convertMsgToPostEntry(const RsGxsChannelGroup& mChannelGroup,const RsMsgMetaData& msg, bool /*useChildTS*/, ChannelPostsModelPostEntry& fentry) +//{ +// fentry.mTitle = msg.mMsgName; +// fentry.mMsgId = msg.mMsgId; +// fentry.mPublishTs = msg.mPublishTs; +// fentry.mPostFlags = 0; +// fentry.mMsgStatus = msg.mMsgStatus; +// +// // Early check for a message that should be hidden because its author +// // is flagged with a bad reputation +//} + +QModelIndex RsGxsChannelPostFilesModel::getIndexOfFile(const RsFileHash& hash) const +{ + // Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map. + + for(uint32_t i=1;i& entries,ForumModelIndex index,int depth) +{ + const ForumModelPostEntry& e(entries[index]); + + QDateTime qtime; + qtime.setTime_t(e.mPublishTs); + + std::cerr << std::string(depth*2,' ') << index << " : " << e.mAuthorId.toStdString() << " " + << QString("%1").arg((uint32_t)e.mPostFlags,8,16,QChar('0')).toStdString() << " " + << QString("%1").arg((uint32_t)e.mMsgStatus,8,16,QChar('0')).toStdString() << " " + << qtime.toString().toStdString() << " \"" << e.mTitle << "\"" << std::endl; + + for(uint32_t i=0;i= mPosts.size()) + return ; + + std::cerr << "Setting own opinion for author " << mPosts[entry].mAuthorId + << " to " << static_cast(op) << std::endl; + RsGxsId author_id = mPosts[entry].mAuthorId; + + rsReputations->setOwnOpinion(author_id,op) ; + + // update opinions and distribution flags. No need to re-load all posts. + + for(uint32_t i=0;i * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#pragma once + +#include "retroshare/rsfiles.h" +#include "retroshare/rsgxscommon.h" + +#include +#include + +// This class holds the actual hierarchy of posts, represented by identifiers +// It is responsible for auto-updating when necessary and holds a mutex to allow the Model to +// safely access the data. + +// The model contains a post in place 0 that is the parent of all posts. + +typedef uint32_t ChannelPostFilesModelIndex; + +// This class is the item model used by Qt to display the information + +class RsGxsChannelPostFilesModel : public QAbstractItemModel +{ + Q_OBJECT + +public: + explicit RsGxsChannelPostFilesModel(QObject *parent = NULL); + ~RsGxsChannelPostFilesModel(){} + + static const int COLUMN_THREAD_NB_COLUMNS = 0x03; // columns for name, size, percentage completion + +#ifdef TODO + enum Columns { + COLUMN_THREAD_TITLE =0x00, + COLUMN_THREAD_READ =0x01, + COLUMN_THREAD_DATE =0x02, + COLUMN_THREAD_DISTRIBUTION =0x03, + COLUMN_THREAD_AUTHOR =0x04, + COLUMN_THREAD_CONTENT =0x05, + COLUMN_THREAD_MSGID =0x06, + COLUMN_THREAD_DATA =0x07, + }; + + enum Roles{ SortRole = Qt::UserRole+1, + ThreadPinnedRole = Qt::UserRole+2, + MissingRole = Qt::UserRole+3, + StatusRole = Qt::UserRole+4, + UnreadChildrenRole = Qt::UserRole+5, + FilterRole = Qt::UserRole+6, + }; +#endif + +#ifdef TODO + enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00, + SORT_MODE_CHILDREN_PUBLISH_TS = 0x01, + }; +#endif + + QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} + QModelIndex getIndexOfFile(const RsFileHash& hash) const; + + // This method will asynchroneously update the data + void setFiles(const std::list& files); + +#ifdef TODO + void setSortMode(SortMode mode) ; + + void setTextColorRead (QColor color) { mTextColorRead = color;} + void setTextColorUnread (QColor color) { mTextColorUnread = color;} + void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} + void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} + void setTextColorMissing (QColor color) { mTextColorMissing = color;} + void setFilter(int column, const QStringList &strings, uint32_t &count) ; + void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); +#endif + + // Helper functions + + void clear() ; + + // AbstractItemModel functions. + + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + int columnCount(const QModelIndex &parent = QModelIndex()) const override; + bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + + QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; + QModelIndex parent(const QModelIndex& child) const override; + Qt::ItemFlags flags(const QModelIndex& index) const override; + + QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + + // Custom item roles + + QVariant sizeHintRole (int col) const; + QVariant displayRole (const RsGxsFile& fmpe, int col) const; + QVariant toolTipRole (const RsGxsFile& fmpe, int col) const; + QVariant userRole (const RsGxsFile& fmpe, int col) const; +#ifdef TODO + QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; + QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant sortRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; + QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; +#endif + + /*! + * \brief debug_dump + * Dumps the hierarchy of posts in the terminal, to allow checking whether the internal representation is correct. + */ + void debug_dump(); + +signals: + void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI. + +private: +#ifdef TODO + bool mUseChildTS; + bool mFilteringEnabled; + SortMode mSortMode; +#endif + void preMods() ; + void postMods() ; + + quintptr getParentRow(quintptr ref,int& row) const; + quintptr getChildRef(quintptr ref, int index) const; + int getChildrenCount(quintptr ref) const; + bool getFileData(const QModelIndex& i,RsGxsFile& fmpe) const; + + static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref); + static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry); + +#ifdef TODO + static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); +#endif + void initEmptyHierarchy(std::vector &files); + + std::vector mFiles ; // store the list of files for the post +}; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 0bd08b9d0..0ba8fc607 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -26,6 +26,7 @@ #include "GxsChannelPostsWidgetWithModel.h" #include "GxsChannelPostsModel.h" +#include "GxsChannelPostFilesModel.h" #include "ui_GxsChannelPostsWidgetWithModel.h" #include "gui/feeds/GxsChannelPostItem.h" #include "gui/gxs/GxsIdDetails.h" @@ -57,7 +58,7 @@ static const int CHANNEL_TABS_POSTS = 1; #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 -Q_DECLARE_METATYPE(RsGxsChannelPost*) +Q_DECLARE_METATYPE(RsGxsFile) void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { @@ -137,6 +138,73 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM return QSize(W+IMAGE_MARGIN_FACTOR*w,H + 2*h); } +void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const +{ + QString byteUnits[4] = {tr("B"), tr("KB"), tr("MB"), tr("GB")}; + + QStyleOptionViewItem opt = option; + QStyleOptionProgressBarV2 newopt; + QRect pixmapRect; + QPixmap pixmap; + qlonglong fileSize; + double dlspeed, multi; + int seconds,minutes, hours, days; + qlonglong remaining; + QString temp ; + qlonglong completed; + qlonglong downloadtime; + qint64 qi64Value; + + // prepare + painter->save(); + painter->setClipRect(opt.rect); + + RsGxsFile file = index.data(Qt::UserRole).value() ; + + QVariant value = index.data(Qt::TextColorRole); + + if(value.isValid() && qvariant_cast(value).isValid()) + opt.palette.setColor(QPalette::Text, qvariant_cast(value)); + + QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + + if(option.state & QStyle::State_Selected) + painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); + else + painter->setPen(opt.palette.color(cg, QPalette::Text)); + + switch(index.column()) + { + case 0: painter->drawText(option.rect,Qt::AlignLeft,QString::fromUtf8(file.mName.c_str())); + break; + case 1: painter->drawText(option.rect,Qt::AlignLeft,QString::number(file.mSize)); + break; + case 2: { + FileInfo finfo; + if(rsFiles->FileDetails(file.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) + painter->drawText(option.rect,Qt::AlignLeft,QString::number(finfo.transfered)); + } + break; + default: + break; + } +} + +QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const +{ + RsGxsFile file = index.data(Qt::UserRole).value() ; + + QFontMetricsF fm(option.font); + + switch(index.column()) + { + case 0: return QSize(fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); + case 1: return QSize(fm.width(QString::number(file.mSize)),fm.height()); + default: + case 2: return QSize(fm.height() * 20,fm.height()) ; + } +} + /** Constructor */ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : GxsMessageFrameWidget(rsGxsChannels, parent), @@ -145,9 +213,12 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI /* Invoke the Qt Designer generated object setup routine */ ui->setupUi(this); - ui->postsTree->setModel(mThreadModel = new RsGxsChannelPostsModel()); + ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel()); ui->postsTree->setItemDelegate(new ChannelPostDelegate()); + ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel()); + ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); + connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); /* Setup UI helper */ @@ -220,7 +291,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI setAutoDownload(false); settingsChanged(); - mThreadModel->updateChannel(channelId); + mChannelPostsModel->updateChannel(channelId); mEventHandlerId = 0; // Needs to be asynced because this function is called by another thread! @@ -264,13 +335,31 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() if(!index.isValid()) { ui->postDetails_TE->clear(); + ui->postLogo_LB->clear(); + mChannelPostFilesModel->clear(); return; } RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + mChannelPostFilesModel->setFiles(post.mFiles); + std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl; ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS)); + + QPixmap postImage; + + if (post.mThumbnail.mData != NULL) + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL); + else + postImage = QPixmap(CHAN_DEFAULT_IMAGE); + + int W = QFontMetricsF(font()).height() * 8; + + // Using fixed width so that the post will not displace the text when we browse. + + ui->postLogo_LB->setPixmap(postImage); + ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W); } void GxsChannelPostsWidgetWithModel::updateGroupData() @@ -297,7 +386,7 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() RsQThreadUtils::postToObject( [this,groups]() { mGroup = groups[0]; - mThreadModel->updateChannel(groupId()); + mChannelPostsModel->updateChannel(groupId()); insertChannelDetails(mGroup); } ); }); @@ -328,7 +417,7 @@ void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete) #warning todo //saveExpandedItems(mSavedExpandedMessages); - //if(mGroupId != mThreadModel->currentGroupId()) + //if(mGroupId != mChannelPostsModel->currentGroupId()) // mThreadId.clear(); updateGroupData(); @@ -881,7 +970,7 @@ void GxsChannelPostsWidgetWithModel::blank() mStateHelper->setWidgetEnabled(ui->postButton, false); mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); - mThreadModel->clear(); + mChannelPostsModel->clear(); groupNameChanged(QString()); //ui->infoWidget->hide(); @@ -1027,3 +1116,4 @@ void GxsChannelPostsWidgetWithModel::setAllMessagesReadDo(bool read, uint32_t &t token = data.mLastToken; } + diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 246be0715..ca37e3289 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -36,6 +36,21 @@ class GxsChannelPostItem; class QTreeWidgetItem; class FeedItem; class RsGxsChannelPostsModel; +class RsGxsChannelPostFilesModel; + +class ChannelPostFilesDelegate: public QAbstractItemDelegate +{ + Q_OBJECT + + public: + ChannelPostFilesDelegate(QObject *parent=0) : QAbstractItemDelegate(parent){} + virtual ~ChannelPostFilesDelegate(){} + + void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; + QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + + private: +}; class ChannelPostDelegate: public QAbstractItemDelegate { @@ -137,7 +152,8 @@ private: bool mUseThread; RsEventsHandlerId_t mEventHandlerId ; - RsGxsChannelPostsModel *mThreadModel; + RsGxsChannelPostsModel *mChannelPostsModel; + RsGxsChannelPostFilesModel *mChannelPostFilesModel; UIStateHelper *mStateHelper; /* UI - from Designer */ diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 5cea1d692..adbe5fb98 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -421,28 +421,70 @@ p, li { white-space: pre-wrap; } + + + true + + - 2 + 1 Details - - - - 9 - 9 - 955 - 181 - - - + + + + + + + TextLabel + + + true + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + - + Files + + + + + + + + Download selected files + + + + diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 8a1133e02..69dfc6b85 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -1335,6 +1335,7 @@ gxschannels { gui/gxschannels/GxsChannelPostsWidget.h \ gui/gxschannels/GxsChannelPostsWidgetWithModel.h \ gui/gxschannels/GxsChannelPostsModel.h \ + gui/gxschannels/GxsChannelPostFilesModel.h \ gui/gxschannels/GxsChannelFilesWidget.h \ gui/gxschannels/GxsChannelFilesStatusWidget.h \ gui/feeds/GxsChannelGroupItem.h \ @@ -1354,6 +1355,7 @@ gxschannels { gui/gxschannels/GxsChannelPostsWidget.cpp \ gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp \ gui/gxschannels/GxsChannelPostsModel.cpp \ + gui/gxschannels/GxsChannelPostFilesModel.cpp \ gui/gxschannels/GxsChannelFilesWidget.cpp \ gui/gxschannels/GxsChannelFilesStatusWidget.cpp \ gui/gxschannels/GxsChannelGroupDialog.cpp \ From b9c41b31d407ac926accef5b84eb2dc73f6f7a46 Mon Sep 17 00:00:00 2001 From: csoler Date: Fri, 5 Jun 2020 17:34:56 +0200 Subject: [PATCH 07/89] added QStyledItemDelegate based on ChannelFilesStatusWidget. Not working yet. --- .../GxsChannelFilesStatusWidget.cpp | 4 +- .../gxschannels/GxsChannelFilesStatusWidget.h | 2 +- .../gui/gxschannels/GxsChannelFilesWidget.cpp | 2 +- .../GxsChannelPostsWidgetWithModel.cpp | 69 +++++++++++++++---- .../GxsChannelPostsWidgetWithModel.h | 10 ++- .../GxsChannelPostsWidgetWithModel.ui | 27 +++++++- 6 files changed, 93 insertions(+), 21 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp index 6dfe8a673..c28601fed 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp @@ -30,8 +30,8 @@ #include "retroshare/rsfiles.h" -GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent) : - QWidget(parent), mGroupId(groupId), mMessageId(messageId), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget) +GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent) : + QWidget(parent), mFile(file), ui(new Ui::GxsChannelFilesStatusWidget) { ui->setupUi(this); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h index 1fc24437f..3d58cc4d7 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h @@ -34,7 +34,7 @@ class GxsChannelFilesStatusWidget : public QWidget Q_OBJECT public: - explicit GxsChannelFilesStatusWidget(const RsGxsGroupId &groupId, const RsGxsMessageId &messageId, const RsGxsFile &file, QWidget *parent = 0); + explicit GxsChannelFilesStatusWidget(const RsGxsFile &file, QWidget *parent = 0); ~GxsChannelFilesStatusWidget(); private slots: diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp index 5ae905cea..6a975df64 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesWidget.cpp @@ -113,7 +113,7 @@ void GxsChannelFilesWidget::addFiles(const RsGxsChannelPost& post, bool related) ui->treeWidget->addTopLevelItem(treeItem); - QWidget *statusWidget = new GxsChannelFilesStatusWidget(post.mMeta.mGroupId, post.mMeta.mMsgId, file); + QWidget *statusWidget = new GxsChannelFilesStatusWidget(file); ui->treeWidget->setItemWidget(treeItem, COLUMN_STATUS, statusWidget); filterItem(treeItem); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 0ba8fc607..2865e2649 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -24,12 +24,10 @@ #include "retroshare/rsgxscircles.h" -#include "GxsChannelPostsWidgetWithModel.h" -#include "GxsChannelPostsModel.h" -#include "GxsChannelPostFilesModel.h" #include "ui_GxsChannelPostsWidgetWithModel.h" #include "gui/feeds/GxsChannelPostItem.h" #include "gui/gxs/GxsIdDetails.h" +#include "util/misc.h" #include "gui/gxschannels/CreateGxsChannelMsg.h" #include "gui/common/UIStateHelper.h" #include "gui/settings/rsharesettings.h" @@ -40,6 +38,11 @@ #include "util/DateTime.h" #include "util/qtthreadsutils.h" +#include "GxsChannelPostsWidgetWithModel.h" +#include "GxsChannelPostsModel.h" +#include "GxsChannelPostFilesModel.h" +#include "GxsChannelFilesStatusWidget.h" + #include #define CHAN_DEFAULT_IMAGE ":/icons/png/channels.png" @@ -51,6 +54,7 @@ ***/ static const int mTokenTypeGroupData = 1; + static const int CHANNEL_TABS_DETAILS= 0; static const int CHANNEL_TABS_POSTS = 1; @@ -58,6 +62,10 @@ static const int CHANNEL_TABS_POSTS = 1; #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 +#define CHANNEL_FILES_COLUMN_NAME 0 +#define CHANNEL_FILES_COLUMN_SIZE 1 +#define CHANNEL_FILES_COLUMN_FILE 2 + Q_DECLARE_METATYPE(RsGxsFile) void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const @@ -138,8 +146,31 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM return QSize(W+IMAGE_MARGIN_FACTOR*w,H + 2*h); } +QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const +{ + RsGxsFile file = index.data(Qt::UserRole).value() ; + + if(index.column() == CHANNEL_FILES_COLUMN_FILE) + return new GxsChannelFilesStatusWidget(file,parent); + else + return NULL; +} +void ChannelPostFilesDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &/* index */) const +{ + editor->setGeometry(option.rect); +} + +void ChannelPostFilesDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const +{ + // nothing to do here. Is this override needed? +} + void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { + RsGxsFile file = index.data(Qt::UserRole).value() ; + +#ifdef TODO + QString byteUnits[4] = {tr("B"), tr("KB"), tr("MB"), tr("GB")}; QStyleOptionViewItem opt = option; @@ -154,13 +185,14 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI qlonglong completed; qlonglong downloadtime; qint64 qi64Value; +#endif // prepare painter->save(); - painter->setClipRect(opt.rect); + painter->setClipRect(option.rect); +#ifdef TODO RsGxsFile file = index.data(Qt::UserRole).value() ; - QVariant value = index.data(Qt::TextColorRole); if(value.isValid() && qvariant_cast(value).isValid()) @@ -172,21 +204,32 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); else painter->setPen(opt.palette.color(cg, QPalette::Text)); +#endif switch(index.column()) { - case 0: painter->drawText(option.rect,Qt::AlignLeft,QString::fromUtf8(file.mName.c_str())); + case CHANNEL_FILES_COLUMN_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,QString::fromUtf8(file.mName.c_str())); break; - case 1: painter->drawText(option.rect,Qt::AlignLeft,QString::number(file.mSize)); + case CHANNEL_FILES_COLUMN_SIZE: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize))); break; - case 2: { + case CHANNEL_FILES_COLUMN_FILE: { + GxsChannelFilesStatusWidget w(file); + QPixmap pixmap(w.size()); + + w.render(&pixmap); + painter->drawPixmap(option.rect.topLeft(),pixmap); + +#ifdef TODO FileInfo finfo; if(rsFiles->FileDetails(file.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) painter->drawText(option.rect,Qt::AlignLeft,QString::number(finfo.transfered)); +#endif } - break; - default: break; + + default: + painter->drawText(option.rect,Qt::AlignLeft,QString("[No data]")); + break; } } @@ -198,10 +241,10 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con switch(index.column()) { - case 0: return QSize(fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); - case 1: return QSize(fm.width(QString::number(file.mSize)),fm.height()); + case CHANNEL_FILES_COLUMN_NAME: return QSize(fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); + case CHANNEL_FILES_COLUMN_SIZE: return QSize(fm.width(misc::friendlyUnit(qulonglong(file.mSize))),fm.height()); default: - case 2: return QSize(fm.height() * 20,fm.height()) ; + case CHANNEL_FILES_COLUMN_FILE: return GxsChannelFilesStatusWidget(file).size(); } } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index ca37e3289..aee073b09 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -23,7 +23,7 @@ #include -#include +#include #include "gui/gxs/GxsMessageFramePostWidget.h" #include "gui/feeds/FeedHolder.h" @@ -38,17 +38,21 @@ class FeedItem; class RsGxsChannelPostsModel; class RsGxsChannelPostFilesModel; -class ChannelPostFilesDelegate: public QAbstractItemDelegate +class ChannelPostFilesDelegate: public QStyledItemDelegate { Q_OBJECT public: - ChannelPostFilesDelegate(QObject *parent=0) : QAbstractItemDelegate(parent){} + ChannelPostFilesDelegate(QObject *parent=0) : QStyledItemDelegate(parent){} virtual ~ChannelPostFilesDelegate(){} void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const override; + QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; + void setEditorData(QWidget *editor, const QModelIndex &index) const override; + private: }; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index adbe5fb98..b6dfb43d5 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -413,9 +413,27 @@ p, li { white-space: pre-wrap; } Qt::Vertical
+ + QAbstractScrollArea::AdjustToContents + QAbstractItemView::SelectItems + + 0 + + + false + + + false + + + true + + + true + false @@ -475,7 +493,14 @@ p, li { white-space: pre-wrap; } - + + + QAbstractItemView::CurrentChanged + + + true + + From d39c47613141efb5ab367a1c012afe9fc2d1d7d8 Mon Sep 17 00:00:00 2001 From: csoler Date: Fri, 5 Jun 2020 18:07:33 +0200 Subject: [PATCH 08/89] fixed entering editing event. --- .../gxschannels/GxsChannelPostFilesModel.cpp | 26 ++++++++++++++++--- .../gxschannels/GxsChannelPostFilesModel.h | 17 +++++------- .../GxsChannelPostsWidgetWithModel.cpp | 18 +++++-------- .../GxsChannelPostsWidgetWithModel.ui | 9 +------ 4 files changed, 36 insertions(+), 34 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 9098af57b..17554a632 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -65,7 +65,7 @@ void RsGxsChannelPostFilesModel::postMods() { endResetModel(); - emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_THREAD_NB_COLUMNS-1,(void*)NULL)); + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); } #ifdef TODO @@ -107,7 +107,7 @@ int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const { - return COLUMN_THREAD_NB_COLUMNS ; + return COLUMN_FILES_NB_COLUMNS ; } // std::vector > RsGxsChannelPostsModel::getPostVersions(const RsGxsMessageId& mid) const @@ -175,7 +175,7 @@ bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint3 QModelIndex RsGxsChannelPostFilesModel::index(int row, int column, const QModelIndex & parent) const { - if(row < 0 || column < 0 || column >= COLUMN_THREAD_NB_COLUMNS) + if(row < 0 || column < 0 || column >= COLUMN_FILES_NB_COLUMNS) return QModelIndex(); quintptr ref = getChildRef(parent.internalId(),row); @@ -199,7 +199,10 @@ Qt::ItemFlags RsGxsChannelPostFilesModel::flags(const QModelIndex& index) const if (!index.isValid()) return 0; - return QAbstractItemModel::flags(index); + if(index.column() == COLUMN_FILES_FILE) + return QAbstractItemModel::flags(index) | Qt::ItemIsEditable; + else + return QAbstractItemModel::flags(index); } quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const @@ -258,6 +261,21 @@ int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const return 0; } +QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + + switch(section) + { + case COLUMN_FILES_FILE: return QString("Status"); + case COLUMN_FILES_SIZE: return QString("Size"); + case COLUMN_FILES_NAME: return QString("File"); + default: + return QString("[No data]"); + } +} + QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) const { #ifdef DEBUG_CHANNEL_MODEL diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index f42007bdb..111906e64 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -44,20 +44,14 @@ public: explicit RsGxsChannelPostFilesModel(QObject *parent = NULL); ~RsGxsChannelPostFilesModel(){} - static const int COLUMN_THREAD_NB_COLUMNS = 0x03; // columns for name, size, percentage completion - -#ifdef TODO enum Columns { - COLUMN_THREAD_TITLE =0x00, - COLUMN_THREAD_READ =0x01, - COLUMN_THREAD_DATE =0x02, - COLUMN_THREAD_DISTRIBUTION =0x03, - COLUMN_THREAD_AUTHOR =0x04, - COLUMN_THREAD_CONTENT =0x05, - COLUMN_THREAD_MSGID =0x06, - COLUMN_THREAD_DATA =0x07, + COLUMN_FILES_NAME = 0x00, + COLUMN_FILES_SIZE = 0x01, + COLUMN_FILES_FILE = 0x02, + COLUMN_FILES_NB_COLUMNS = 0x03 }; +#ifdef TODO enum Roles{ SortRole = Qt::UserRole+1, ThreadPinnedRole = Qt::UserRole+2, MissingRole = Qt::UserRole+3, @@ -106,6 +100,7 @@ public: Qt::ItemFlags flags(const QModelIndex& index) const override; QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; // Custom item roles diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 2865e2649..4e5ea5077 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -62,10 +62,6 @@ static const int CHANNEL_TABS_POSTS = 1; #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 -#define CHANNEL_FILES_COLUMN_NAME 0 -#define CHANNEL_FILES_COLUMN_SIZE 1 -#define CHANNEL_FILES_COLUMN_FILE 2 - Q_DECLARE_METATYPE(RsGxsFile) void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const @@ -150,7 +146,7 @@ QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOpt { RsGxsFile file = index.data(Qt::UserRole).value() ; - if(index.column() == CHANNEL_FILES_COLUMN_FILE) + if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE) return new GxsChannelFilesStatusWidget(file,parent); else return NULL; @@ -208,11 +204,11 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI switch(index.column()) { - case CHANNEL_FILES_COLUMN_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,QString::fromUtf8(file.mName.c_str())); + case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,QString::fromUtf8(file.mName.c_str())); break; - case CHANNEL_FILES_COLUMN_SIZE: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize))); + case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: painter->drawText(option.rect,Qt::AlignRight | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize))); break; - case CHANNEL_FILES_COLUMN_FILE: { + case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: { GxsChannelFilesStatusWidget w(file); QPixmap pixmap(w.size()); @@ -241,10 +237,10 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con switch(index.column()) { - case CHANNEL_FILES_COLUMN_NAME: return QSize(fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); - case CHANNEL_FILES_COLUMN_SIZE: return QSize(fm.width(misc::friendlyUnit(qulonglong(file.mSize))),fm.height()); + case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return QSize(fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); + case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return QSize(fm.width(misc::friendlyUnit(qulonglong(file.mSize))),fm.height()); default: - case CHANNEL_FILES_COLUMN_FILE: return GxsChannelFilesStatusWidget(file).size(); + case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: return GxsChannelFilesStatusWidget(file).size(); } } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index b6dfb43d5..18e59ff2e 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -495,20 +495,13 @@ p, li { white-space: pre-wrap; } - QAbstractItemView::CurrentChanged + QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked true - - - - Download selected files - - - From facad382cf319d6abaa4fb512b401076eaf8ff76 Mon Sep 17 00:00:00 2001 From: csoler Date: Fri, 5 Jun 2020 22:43:43 +0200 Subject: [PATCH 09/89] fixed a few UI glitches --- .../gxschannels/GxsChannelPostFilesModel.cpp | 39 ++++++----------- .../gxschannels/GxsChannelPostFilesModel.h | 7 +++ .../GxsChannelPostsWidgetWithModel.cpp | 43 ++++++++++++------- .../GxsChannelPostsWidgetWithModel.h | 3 -- .../GxsChannelPostsWidgetWithModel.ui | 19 ++++++++ 5 files changed, 67 insertions(+), 44 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 17554a632..ab20c2fd8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -43,6 +43,9 @@ RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent) : QAbstractItemModel(parent) { initEmptyHierarchy(mFiles); + + mTimer = new QTimer; + connect(mTimer,SIGNAL(timeout()),this,SLOT(update())); } void RsGxsChannelPostFilesModel::initEmptyHierarchy(std::vector& files) @@ -68,6 +71,11 @@ void RsGxsChannelPostFilesModel::postMods() emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); } +void RsGxsChannelPostFilesModel::update() +{ + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); +} + #ifdef TODO void RsGxsChannelPostsModel::setSortMode(SortMode mode) { @@ -694,34 +702,13 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list& files) postMods(); emit channelLoaded(); + + if(!files.empty()) + mTimer->start(5000); + else + mTimer->stop(); } -//ChannelPostsModelIndex RsGxsChannelPostsModel::addEntry(std::vector& posts,const ChannelPostsModelPostEntry& entry) -//{ -// uint32_t N = posts.size(); -// posts.push_back(entry); -// -//#ifdef DEBUG_FORUMMODEL -// std::cerr << "Added new entry " << N << " children of " << parent << std::endl; -//#endif -// if(N == parent) -// std::cerr << "(EE) trying to add a post as its own parent!" << std::endl; -// -// return ChannelPostsModelIndex(N); -//} - -//void RsGxsChannelPostsModel::convertMsgToPostEntry(const RsGxsChannelGroup& mChannelGroup,const RsMsgMetaData& msg, bool /*useChildTS*/, ChannelPostsModelPostEntry& fentry) -//{ -// fentry.mTitle = msg.mMsgName; -// fentry.mMsgId = msg.mMsgId; -// fentry.mPublishTs = msg.mPublishTs; -// fentry.mPostFlags = 0; -// fentry.mMsgStatus = msg.mMsgStatus; -// -// // Early check for a message that should be hidden because its author -// // is flagged with a bad reputation -//} - QModelIndex RsGxsChannelPostFilesModel::getIndexOfFile(const RsFileHash& hash) const { // Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map. diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index 111906e64..873f179e8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -34,6 +34,8 @@ typedef uint32_t ChannelPostFilesModelIndex; +class QTimer; + // This class is the item model used by Qt to display the information class RsGxsChannelPostFilesModel : public QAbstractItemModel @@ -130,6 +132,9 @@ public: signals: void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI. +private slots: + void update(); + private: #ifdef TODO bool mUseChildTS; @@ -153,4 +158,6 @@ private: void initEmptyHierarchy(std::vector &files); std::vector mFiles ; // store the list of files for the post + + QTimer *mTimer; }; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 4e5ea5077..d0dc70e96 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -156,11 +156,6 @@ void ChannelPostFilesDelegate::updateEditorGeometry(QWidget *editor, const QStyl editor->setGeometry(option.rect); } -void ChannelPostFilesDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const -{ - // nothing to do here. Is this override needed? -} - void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { RsGxsFile file = index.data(Qt::UserRole).value() ; @@ -187,6 +182,12 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI painter->save(); painter->setClipRect(option.rect); + painter->save(); + + painter->fillRect( option.rect, option.backgroundBrush); + //optionFocusRect.backgroundColor = option.palette.color(colorgroup, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); + painter->restore(); + #ifdef TODO RsGxsFile file = index.data(Qt::UserRole).value() ; QVariant value = index.data(Qt::TextColorRole); @@ -194,25 +195,30 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI if(value.isValid() && qvariant_cast(value).isValid()) opt.palette.setColor(QPalette::Text, qvariant_cast(value)); - QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; + QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; if(option.state & QStyle::State_Selected) - painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); + painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); else - painter->setPen(opt.palette.color(cg, QPalette::Text)); + painter->setPen(option.palette.color(cg, QPalette::Text)); #endif switch(index.column()) { - case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,QString::fromUtf8(file.mName.c_str())); + case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter," " + QString::fromUtf8(file.mName.c_str())); break; case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: painter->drawText(option.rect,Qt::AlignRight | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize))); break; case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: { - GxsChannelFilesStatusWidget w(file); - QPixmap pixmap(w.size()); - w.render(&pixmap); + GxsChannelFilesStatusWidget w(file); + + w.setFixedWidth(option.rect.width()); + + QPixmap pixmap(w.size()); + pixmap.fill(option.palette.color(QPalette::Background)); // choose the background + w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background + painter->drawPixmap(option.rect.topLeft(),pixmap); #ifdef TODO @@ -237,10 +243,10 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con switch(index.column()) { - case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return QSize(fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); - case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return QSize(fm.width(misc::friendlyUnit(qulonglong(file.mSize))),fm.height()); + case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return QSize(1.1*fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); + case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return QSize(1.1*fm.width(misc::friendlyUnit(qulonglong(file.mSize))),fm.height()); default: - case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: return GxsChannelFilesStatusWidget(file).size(); + case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: return QSize(option.rect.width(),GxsChannelFilesStatusWidget(file).height()); } } @@ -398,7 +404,14 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() // Using fixed width so that the post will not displace the text when we browse. ui->postLogo_LB->setPixmap(postImage); + ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); + ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W); + ui->postName_LB->setFixedWidth(W); + + ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); + ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); + ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); } void GxsChannelPostsWidgetWithModel::updateGroupData() diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index aee073b09..1aa555caf 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -51,9 +51,6 @@ class ChannelPostFilesDelegate: public QStyledItemDelegate void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &) const override; QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override; - void setEditorData(QWidget *editor, const QModelIndex &index) const override; - - private: }; class ChannelPostDelegate: public QAbstractItemDelegate diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 18e59ff2e..0332379b0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -465,6 +465,19 @@ p, li { white-space: pre-wrap; } Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + true + + + + + + + TextLabel + + + true + @@ -500,6 +513,12 @@ p, li { white-space: pre-wrap; } true + + 5 + + + false + From 89fc77ef8e3af4a520ab5ca65fc5ec575d491f35 Mon Sep 17 00:00:00 2001 From: csoler Date: Fri, 5 Jun 2020 22:47:29 +0200 Subject: [PATCH 10/89] fixed bug causing crash when clicking on non channel entries --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index d0dc70e96..d3f952e96 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -1019,8 +1019,8 @@ void GxsChannelPostsWidget::clearPosts() void GxsChannelPostsWidgetWithModel::blank() { - mStateHelper->setWidgetEnabled(ui->postButton, false); - mStateHelper->setWidgetEnabled(ui->subscribeToolButton, false); + ui->postButton->setEnabled(false); + ui->subscribeToolButton->setEnabled(false); mChannelPostsModel->clear(); groupNameChanged(QString()); From 3106157ab0a2f371166ff7617d5582ae8fc40598 Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 6 Jun 2020 13:58:14 +0200 Subject: [PATCH 11/89] fixed layout for thumbnails --- .../GxsChannelPostsWidgetWithModel.cpp | 102 +++++++++++++++--- .../GxsChannelPostsWidgetWithModel.h | 1 - .../GxsChannelPostsWidgetWithModel.ui | 3 + 3 files changed, 91 insertions(+), 15 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index d3f952e96..fd9ddedff 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -61,11 +61,59 @@ static const int CHANNEL_TABS_POSTS = 1; /* View mode */ #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 +#define COLUMN_SIZE_FONT_FACTOR_W 6 +#define COLUMN_SIZE_FONT_FACTOR_H 12 Q_DECLARE_METATYPE(RsGxsFile) +// Class to paint the thumbnails with title + +class ThumbnailView: public QWidget +{ +public: + ThumbnailView(const RsGxsChannelPost& post,QWidget *parent=NULL) + : QWidget(parent) + { + QVBoxLayout *layout = new QVBoxLayout(this); + + QLabel *lb = new QLabel(this); + lb->setScaledContents(true); + layout->addWidget(lb); + + QLabel *lt = new QLabel(this); + layout->addWidget(lt); + + setLayout(layout); + + setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding); + + // now fill the data + + QPixmap thumbnail; + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); + + lb->setPixmap(thumbnail); + + QFontMetricsF fm(font()); + int W = 8 * fm.height() ; + int H = 12 * fm.height() ; + + lb->setFixedSize(W,H); + + lt->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); + lt->setMaximumWidth(W); + lt->setWordWrap(true); + + adjustSize(); + update(); + } +}; + +// Delegate used to paint into the table of thumbnails + void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { +#ifdef TODO QString byteUnits[4] = {tr("B"), tr("KB"), tr("MB"), tr("GB")}; QStyleOptionViewItem opt = option; QStyleOptionProgressBarV2 newopt; @@ -79,14 +127,16 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & qlonglong completed; qlonglong downloadtime; qint64 qi64Value; +#endif // prepare painter->save(); - painter->setClipRect(opt.rect); + painter->setClipRect(option.rect); RsGxsChannelPost post = index.data(Qt::UserRole).value() ; //const RsGxsChannelPost& post(*pinfo); +#ifdef TODO QVariant value = index.data(Qt::TextColorRole); if(value.isValid() && qvariant_cast(value).isValid()) @@ -122,12 +172,42 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & painter->drawPixmap(QRect(opt.rect.topLeft() + img_pos,opt.rect.topLeft()+img_pos+img_size),thumbnail.scaled(W,H,Qt::KeepAspectRatio,Qt::SmoothTransformation)); painter->drawText(QRect(option.rect.topLeft() + txt_pos,option.rect.bottomRight()),Qt::AlignCenter,QString::fromStdString(post.mMeta.mMsgName)); +#endif + + painter->save(); + painter->fillRect( option.rect, option.backgroundBrush); + painter->restore(); + + ThumbnailView w(post); + + //w.setFixedWidth(option.rect.width()); + + QPixmap pixmap(w.size()); + pixmap.fill(option.palette.color(QPalette::Background)); // choose the background + w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background + + // debug + if(index.row()==0 && index.column()==0) + { + QFile file("yourFile.png"); + file.open(QIODevice::WriteOnly); + pixmap.save(&file, "PNG"); + std::cerr << "Saved pxmap to png" << std::endl; + } + std::cerr << "option.rect = " << option.rect.width() << "x" << option.rect.height() << ". fm.height()=" << QFontMetricsF(option.font).height() << std::endl; + painter->drawPixmap(option.rect.topLeft(), + pixmap.scaled(option.rect.width(),option.rect.width()*w.height()/(float)w.width(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); } QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + // This is the only place where we actually set the size of cells + QFontMetricsF fm(option.font); + + return QSize(COLUMN_SIZE_FONT_FACTOR_W*fm.height(),COLUMN_SIZE_FONT_FACTOR_H*fm.height()); + +#ifdef TODO //QPixmap thumbnail; //GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); @@ -140,6 +220,7 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM int w = fm.width("X") ; return QSize(W+IMAGE_MARGIN_FACTOR*w,H + 2*h); +#endif } QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const @@ -266,20 +347,13 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); + QFontMetricsF fm(font()); + + for(int i=0;icolumnCount();++i) + ui->postsTree->setColumnWidth(i,COLUMN_SIZE_FONT_FACTOR_W*fm.height()); + /* Setup UI helper */ - //mStateHelper->addWidget(mTokenTypeAllPosts, ui->progressBar, UISTATE_LOADING_VISIBLE); - //mStateHelper->addWidget(mTokenTypeAllPosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); - //mStateHelper->addWidget(mTokenTypeAllPosts, ui->filterLineEdit); - - //mStateHelper->addWidget(mTokenTypePosts, ui->loadingLabel, UISTATE_LOADING_VISIBLE); - - //mStateHelper->addLoadPlaceholder(mTokenTypeGroupData, ui->nameLabel); - - //mStateHelper->addWidget(mTokenTypeGroupData, ui->postButton); - //mStateHelper->addWidget(mTokenTypeGroupData, ui->logoLabel); - //mStateHelper->addWidget(mTokenTypeGroupData, ui->subscribeToolButton); - /* Connect signals */ connect(ui->postButton, SIGNAL(clicked()), this, SLOT(createMsg())); connect(ui->subscribeToolButton, SIGNAL(subscribe(bool)), this, SLOT(subscribeGroup(bool))); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 1aa555caf..51bc79b6b 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -155,7 +155,6 @@ private: RsGxsChannelPostsModel *mChannelPostsModel; RsGxsChannelPostFilesModel *mChannelPostFilesModel; - UIStateHelper *mStateHelper; /* UI - from Designer */ Ui::GxsChannelPostsWidgetWithModel *ui; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 0332379b0..66c4d4ba3 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -419,6 +419,9 @@ p, li { white-space: pre-wrap; } QAbstractItemView::SelectItems + + Qt::ElideNone + 0 From 129ffdd6c5021a1eb358dcc3e8f29e0b16e0906c Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 6 Jun 2020 19:07:03 +0200 Subject: [PATCH 12/89] fixed auto-layout of thumbnails --- retroshare-gui/src/gui/common/RSTreeView.cpp | 6 ++ retroshare-gui/src/gui/common/RSTreeView.h | 6 +- .../GxsChannelFilesStatusWidget.ui | 4 +- .../gxschannels/GxsChannelPostFilesModel.cpp | 2 +- .../gui/gxschannels/GxsChannelPostsModel.cpp | 21 +++++- .../gui/gxschannels/GxsChannelPostsModel.h | 1 + .../GxsChannelPostsWidgetWithModel.cpp | 64 ++++++++++++++----- .../GxsChannelPostsWidgetWithModel.h | 2 + .../GxsChannelPostsWidgetWithModel.ui | 9 ++- 9 files changed, 89 insertions(+), 26 deletions(-) diff --git a/retroshare-gui/src/gui/common/RSTreeView.cpp b/retroshare-gui/src/gui/common/RSTreeView.cpp index 918fbc558..da1aa5840 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.cpp +++ b/retroshare-gui/src/gui/common/RSTreeView.cpp @@ -19,12 +19,18 @@ *******************************************************************************/ #include +#include #include "RSTreeView.h" RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent) { } +void RSTreeView::resizeEvent(QResizeEvent *e) +{ + emit sizeChanged(e->size()); +} + void RSTreeView::setPlaceholderText(const QString &text) { placeholderText = text; diff --git a/retroshare-gui/src/gui/common/RSTreeView.h b/retroshare-gui/src/gui/common/RSTreeView.h index 022a643db..f895571f0 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.h +++ b/retroshare-gui/src/gui/common/RSTreeView.h @@ -33,8 +33,12 @@ public: void setPlaceholderText(const QString &text); +signals: + void sizeChanged(QSize); + protected: - void paintEvent(QPaintEvent *event); + virtual void resizeEvent(QResizeEvent *e) override; + virtual void paintEvent(QPaintEvent *event) override; QString placeholderText; }; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui index 59fa846da..39e32271d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui @@ -6,8 +6,8 @@ 0 0 - 367 - 27 + 421 + 29 diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index ab20c2fd8..06ba27a0f 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -33,7 +33,7 @@ #include "GxsChannelPostFilesModel.h" -#define DEBUG_CHANNEL_MODEL +//#define DEBUG_CHANNEL_MODEL Q_DECLARE_METATYPE(RsGxsFile) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 5d82110d5..b7649af26 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -32,7 +32,7 @@ #include "retroshare/rsgxschannels.h" #include "retroshare/rsexpr.h" -#define DEBUG_CHANNEL_MODEL +//#define DEBUG_CHANNEL_MODEL Q_DECLARE_METATYPE(RsMsgMetaData) @@ -221,6 +221,21 @@ Qt::ItemFlags RsGxsChannelPostsModel::flags(const QModelIndex& index) const return QAbstractItemModel::flags(index); } +void RsGxsChannelPostsModel::setNumColumns(int n) +{ + preMods(); + + beginRemoveRows(QModelIndex(),0,rowCount()-1); + endRemoveRows(); + + mColumns = n; + + beginInsertRows(QModelIndex(),0,rowCount()-1); + endInsertRows(); + + postMods(); +} + quintptr RsGxsChannelPostsModel::getChildRef(quintptr ref,int index) const { if (index < 0) @@ -675,7 +690,7 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto { preMods(); - beginRemoveRows(QModelIndex(),0,mPosts.size()-1); + beginRemoveRows(QModelIndex(),0,rowCount()-1); endRemoveRows(); mPosts.clear(); @@ -692,7 +707,7 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto // debug_dump(); #endif - beginInsertRows(QModelIndex(),0,mPosts.size()-1); + beginInsertRows(QModelIndex(),0,rowCount()-1); endInsertRows(); postMods(); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h index de4713e79..60b4065de 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h @@ -110,6 +110,7 @@ public: const RsGxsGroupId& currentGroupId() const; void setTreeMode(TreeMode mode) ; + void setNumColumns(int n); #ifdef TODO void setSortMode(SortMode mode) ; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index fd9ddedff..8bfbb5e3d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -61,8 +61,21 @@ static const int CHANNEL_TABS_POSTS = 1; /* View mode */ #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 + +// Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good. + +#define THUMBNAIL_W 4 +#define THUMBNAIL_H 6 + +// Determine the Shape and size of cells as a factor of the font height. An aspect ratio of 3/4 is what's needed +// for the image, so it's important that the height is a bit larger so as to leave some room for the text. +// +// #define COLUMN_SIZE_FONT_FACTOR_W 6 -#define COLUMN_SIZE_FONT_FACTOR_H 12 +#define COLUMN_SIZE_FONT_FACTOR_H 10 + +// This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen. +#define THUMBNAIL_OVERSAMPLE_FACTOR 2.0 Q_DECLARE_METATYPE(RsGxsFile) @@ -95,8 +108,8 @@ public: lb->setPixmap(thumbnail); QFontMetricsF fm(font()); - int W = 8 * fm.height() ; - int H = 12 * fm.height() ; + int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ; + int H = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_H * fm.height() ; lb->setFixedSize(W,H); @@ -183,18 +196,19 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & //w.setFixedWidth(option.rect.width()); QPixmap pixmap(w.size()); - pixmap.fill(option.palette.color(QPalette::Background)); // choose the background + pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background // debug - if(index.row()==0 && index.column()==0) - { - QFile file("yourFile.png"); - file.open(QIODevice::WriteOnly); - pixmap.save(&file, "PNG"); - std::cerr << "Saved pxmap to png" << std::endl; - } - std::cerr << "option.rect = " << option.rect.width() << "x" << option.rect.height() << ". fm.height()=" << QFontMetricsF(option.font).height() << std::endl; + // if(index.row()==0 && index.column()==0) + // { + // QFile file("yourFile.png"); + // file.open(QIODevice::WriteOnly); + // pixmap.save(&file, "PNG"); + // std::cerr << "Saved pxmap to png" << std::endl; + // } + //std::cerr << "option.rect = " << option.rect.width() << "x" << option.rect.height() << ". fm.height()=" << QFontMetricsF(option.font).height() << std::endl; + painter->drawPixmap(option.rect.topLeft(), pixmap.scaled(option.rect.width(),option.rect.width()*w.height()/(float)w.width(),Qt::IgnoreAspectRatio,Qt::SmoothTransformation)); } @@ -297,7 +311,7 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI w.setFixedWidth(option.rect.width()); QPixmap pixmap(w.size()); - pixmap.fill(option.palette.color(QPalette::Background)); // choose the background + pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background painter->drawPixmap(option.rect.topLeft(),pixmap); @@ -344,6 +358,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel()); ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); + ui->channelPostFiles_TV->setPlaceholderText(tr("Post files")); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); @@ -386,6 +401,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->loadingLabel->hide(); ui->progressBar->hide(); + ui->postsTree->setPlaceholderText(tr("Thumbnails")); + ui->postsTree->setMinimumWidth(COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()+1); //ui->nameLabel->setMinimumWidth(20); @@ -414,10 +431,23 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI mEventHandlerId = 0; // Needs to be asynced because this function is called by another thread! - rsEvents->registerEventsHandler( - [this](std::shared_ptr event) - { RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); }, - mEventHandlerId, RsEventType::GXS_CHANNELS ); + rsEvents->registerEventsHandler( [this](std::shared_ptr event) + { + RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); + }, mEventHandlerId, RsEventType::GXS_CHANNELS ); + + connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize))); +} + +void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s) +{ +// adjustSize(); +// + int n_columns = std::max(1,(int)floor(s.width() / (COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()))); + std::cerr << "nb columns: " << n_columns << std::endl; + + if(n_columns != mChannelPostsModel->columnCount()) + mChannelPostsModel->setNumColumns(n_columns); } void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptr event) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 51bc79b6b..48caeaf49 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -108,6 +108,7 @@ public: protected: /* GxsMessageFramePostWidget */ virtual void groupNameChanged(const QString &name); + #ifdef TODO virtual bool insertGroupData(const RsGxsGenericGroupData *data) override; #endif @@ -134,6 +135,7 @@ private slots: void filterChanged(int filter); void setViewMode(int viewMode); void settingsChanged(); + void handlePostsTreeSizeChange(QSize s); private: void processSettings(bool load); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 66c4d4ba3..0f3405cda 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -412,7 +412,7 @@ p, li { white-space: pre-wrap; } Qt::Vertical - + QAbstractScrollArea::AdjustToContents @@ -509,7 +509,7 @@ p, li { white-space: pre-wrap; } - + QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked @@ -566,6 +566,11 @@ p, li { white-space: pre-wrap; } QLineEdit
gui/common/LineEditClear.h
+ + RSTreeView + QTreeView +
gui/common/RSTreeView.h
+
From fecd4369ec7e436bceffd44219dd202823ac455e Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 6 Jun 2020 21:32:06 +0200 Subject: [PATCH 13/89] fixed a few bugs in posts model --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 67 ++++++++----------- .../GxsChannelPostsWidgetWithModel.cpp | 3 + 2 files changed, 32 insertions(+), 38 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index b7649af26..e0af09eee 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -115,13 +115,10 @@ int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const return 0; if(!parent.isValid()) - return (getChildrenCount(0) + mColumns-1)/mColumns; + return (mPosts.size()-1 + mColumns-1)/mColumns; // mPosts always has an item at 0, so size()>=1, and mColumn>=1 RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; return 0; - - //else - // return getChildrenCount(parent.internalId()); } int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const @@ -173,7 +170,7 @@ bool RsGxsChannelPostsModel::convertTabEntryToRefPointer(uint32_t entry,quintptr // This means that the whole software has the following build-in limitation: // * 4 B simultaenous posts. Should be enough ! - ref = (intptr_t)entry; + ref = (intptr_t)(entry+1); return true; } @@ -187,7 +184,13 @@ bool RsGxsChannelPostsModel::convertRefPointerToTabEntry(quintptr ref, uint32_t& RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl; return false ; } - entry = quintptr(val); + if(val==0) + { + RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of index 0." << std::endl; + return false; + } + + entry = val - 1; return true; } @@ -197,7 +200,7 @@ QModelIndex RsGxsChannelPostsModel::index(int row, int column, const QModelIndex if(row < 0 || column < 0 || column >= mColumns) return QModelIndex(); - quintptr ref = getChildRef(parent.internalId(),row + column*mColumns); + quintptr ref = getChildRef(parent.internalId(),column + row*mColumns); #ifdef DEBUG_CHANNEL_MODEL std::cerr << "index-3(" << row << "," << column << " parent=" << parent << ") : " << createIndex(row,column,ref) << std::endl; @@ -223,7 +226,12 @@ Qt::ItemFlags RsGxsChannelPostsModel::flags(const QModelIndex& index) const void RsGxsChannelPostsModel::setNumColumns(int n) { - preMods(); + if(n < 1) + { + RsErr() << __PRETTY_FUNCTION__ << " Attempt to set a number of column of 0. This is wrong." << std::endl; + return; + } + preMods(); beginRemoveRows(QModelIndex(),0,rowCount()-1); endRemoveRows(); @@ -241,15 +249,10 @@ quintptr RsGxsChannelPostsModel::getChildRef(quintptr ref,int index) const if (index < 0) return 0; - ChannelPostsModelIndex entry ; - - if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) - return 0 ; - - if(entry == 0) + if(ref == quintptr(0)) { quintptr new_ref; - convertTabEntryToRefPointer(index+1,new_ref); + convertTabEntryToRefPointer(index,new_ref); return new_ref; } else @@ -278,18 +281,10 @@ int RsGxsChannelPostsModel::getChildrenCount(quintptr ref) const { uint32_t entry = 0 ; - if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) - return 0 ; + if(ref == quintptr(0)) + return rowCount()-1; - if(entry == 0) - { -#ifdef DEBUG_CHANNEL_MODEL - std::cerr << "Children count (flat mode): " << mPosts.size()-1 << std::endl; -#endif - return ((int)mPosts.size())-1; - } - else - return 0; + return 0; } QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const @@ -803,7 +798,7 @@ void RsGxsChannelPostsModel::createPostsArray(std::vector& pos { // collect new versions of posts if any -#ifdef DEBUG_CHANNEL_MODEL +#ifndef DEBUG_CHANNEL_MODEL std::cerr << "Inserting channel posts" << std::endl; #endif @@ -813,8 +808,8 @@ void RsGxsChannelPostsModel::createPostsArray(std::vector& pos if(posts[i].mMeta.mOrigMsgId == posts[i].mMeta.mMsgId) posts[i].mMeta.mOrigMsgId.clear(); -#ifdef DEBUG_CHANNEL_MODEL - std::cerr << " " << i << ": msg_id=" << posts[i].mMeta.mMsgId << ": orig msg id = " << posts[i].mMeta.mOrigMsgId << std::endl; +#ifndef DEBUG_CHANNEL_MODEL + std::cerr << " " << i << ": name=\"" << posts[i].mMeta.mMsgName << "\" msg_id=" << posts[i].mMeta.mMsgId << ": orig msg id = " << posts[i].mMeta.mOrigMsgId << std::endl; #endif if(!posts[i].mMeta.mOrigMsgId.isNull()) @@ -903,20 +898,16 @@ void RsGxsChannelPostsModel::createPostsArray(std::vector& pos for (std::vector::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it) { -#ifdef DEBUG_CHANNEL_MODEL - std::cerr << " adding post: " << (*it).mMeta.mMsgId ; -#endif - if(!(*it).mMeta.mMsgId.isNull()) { -#ifdef DEBUG_CHANNEL_MODEL - std::cerr << " added" << std::endl; +#ifndef DEBUG_CHANNEL_MODEL + std::cerr << " adding post \"" << (*it).mMeta.mMsgName << "\"" << std::endl; #endif mPosts.push_back(*it); } #ifdef DEBUG_CHANNEL_MODEL else - std::cerr << " skipped" << std::endl; + std::cerr << " skipped older version post \"" << (*it).mMeta.mMsgName << "\"" << std::endl; #endif } } @@ -1025,7 +1016,7 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) quintptr ref ; convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab - return createIndex(i-1,0,ref); + return createIndex((i-1)%mColumns, (i-1)/mColumns,ref); } if(mPosts[i].mMeta.mMsgId == postId) @@ -1033,7 +1024,7 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) quintptr ref ; convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab - return createIndex(i-1,0,ref); + return createIndex((i-1)%mColumns, (i-1)/mColumns,ref); } } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 8bfbb5e3d..0b4463f80 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -488,6 +488,9 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() mChannelPostFilesModel->clear(); return; } + if(index.row()==0 && index.column()==0) + std::cerr << "here" << std::endl; + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; mChannelPostFilesModel->setFiles(post.mFiles); From baf7b06e4ce71ff7b3e31e4e49ce2b9b14e7756e Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 6 Jun 2020 21:51:40 +0200 Subject: [PATCH 14/89] fixed double click problem on files --- retroshare-gui/src/gui/common/RSTreeView.cpp | 19 + retroshare-gui/src/gui/common/RSTreeView.h | 6 + .../gui/gxschannels/GxsChannelPostsModel.cpp | 502 +----------------- .../GxsChannelPostsWidgetWithModel.cpp | 1 + 4 files changed, 29 insertions(+), 499 deletions(-) diff --git a/retroshare-gui/src/gui/common/RSTreeView.cpp b/retroshare-gui/src/gui/common/RSTreeView.cpp index da1aa5840..36034166f 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.cpp +++ b/retroshare-gui/src/gui/common/RSTreeView.cpp @@ -24,6 +24,25 @@ RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent) { + setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future. +} + +void RSTreeView::mouseMoveEvent(QMouseEvent *e) +{ + QModelIndex idx = indexAt(e->pos()); + + if(idx != selectionModel()->currentIndex()) + selectionModel()->setCurrentIndex(idx,QItemSelectionModel::ClearAndSelect); + + QTreeView::mouseMoveEvent(e); +} + +void RSTreeView::setAutoSelect(bool b) +{ + if(b) + setMouseTracking(true); + else + setMouseTracking(false); } void RSTreeView::resizeEvent(QResizeEvent *e) diff --git a/retroshare-gui/src/gui/common/RSTreeView.h b/retroshare-gui/src/gui/common/RSTreeView.h index f895571f0..f7942ef64 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.h +++ b/retroshare-gui/src/gui/common/RSTreeView.h @@ -33,10 +33,16 @@ public: void setPlaceholderText(const QString &text); + // Use this to make selection automatic based on mouse position. This is useful to trigger selection and therefore editing mode + // in trees that show editing widgets using a QStyledItemDelegate + + void setAutoSelect(bool b); + signals: void sizeChanged(QSize); protected: + virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection virtual void resizeEvent(QResizeEvent *e) override; virtual void paintEvent(QPaintEvent *event) override; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index e0af09eee..6fc4325a5 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -84,28 +84,6 @@ void RsGxsChannelPostsModel::setTreeMode(TreeMode mode) postMods(); } -#ifdef TODO -void RsGxsChannelPostsModel::setSortMode(SortMode mode) -{ - preMods(); - - mSortMode = mode; - - postMods(); -} - -void RsGxsForumModel::initEmptyHierarchy(std::vector& posts) -{ - preMods(); - - posts.resize(1); // adds a sentinel item - posts[0].mTitle = "Root sentinel post" ; - posts[0].mParent = 0; - - postMods(); -} -#endif - int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const { if(parent.column() > 0) @@ -126,16 +104,6 @@ int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const return mColumns ; } -// std::vector > RsGxsChannelPostsModel::getPostVersions(const RsGxsMessageId& mid) const -// { -// auto it = mPostVersions.find(mid); -// -// if(it != mPostVersions.end()) -// return it->second; -// else -// return std::vector >(); -// } - bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const { if(!i.isValid()) @@ -328,303 +296,28 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const const RsGxsChannelPost& fmpe(mPosts[entry]); -#ifdef TODO - if(role == Qt::FontRole) - { - QFont font ; - font.setBold( (fmpe.mPostFlags & (ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN | ForumModelPostEntry::FLAG_POST_IS_PINNED)) || IS_MSG_UNREAD(fmpe.mMsgStatus)); - return QVariant(font); - } - - if(role == UnreadChildrenRole) - return bool(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN); - -#ifdef DEBUG_CHANNEL_MODEL - std::cerr << " [ok]" << std::endl; -#endif -#endif - switch(role) { case Qt::DisplayRole: return displayRole (fmpe,index.column()) ; case Qt::UserRole: return userRole (fmpe,index.column()) ; -#ifdef TODO - case Qt::DecorationRole: return decorationRole(fmpe,index.column()) ; - case Qt::ToolTipRole: return toolTipRole (fmpe,index.column()) ; - case Qt::TextColorRole: return textColorRole (fmpe,index.column()) ; - case Qt::BackgroundRole: return backgroundRole(fmpe,index.column()) ; - - case FilterRole: return filterRole (fmpe,index.column()) ; - case ThreadPinnedRole: return pinnedRole (fmpe,index.column()) ; - case MissingRole: return missingRole (fmpe,index.column()) ; - case StatusRole: return statusRole (fmpe,index.column()) ; - case SortRole: return sortRole (fmpe,index.column()) ; -#endif default: return QVariant(); } } -#ifdef TODO -QVariant RsGxsForumModel::textColorRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if( (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING)) - return QVariant(mTextColorMissing); - - if(IS_MSG_UNREAD(fmpe.mMsgStatus) || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)) - return QVariant(mTextColorUnread); - else - return QVariant(mTextColorRead); - - return QVariant(); -} - -QVariant RsGxsForumModel::statusRole(const ForumModelPostEntry& fmpe,int column) const -{ - if(column != COLUMN_THREAD_DATA) - return QVariant(); - - return QVariant(fmpe.mMsgStatus); -} - -QVariant RsGxsForumModel::filterRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(!mFilteringEnabled || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER)) - return QVariant(FilterString); - - return QVariant(QString()); -} - -uint32_t RsGxsForumModel::recursUpdateFilterStatus(ForumModelIndex i,int column,const QStringList& strings) -{ - QString s ; - uint32_t count = 0; - - switch(column) - { - default: - case COLUMN_THREAD_DATE: - case COLUMN_THREAD_TITLE: s = displayRole(mPosts[i],column).toString(); - break; - case COLUMN_THREAD_AUTHOR: - { - QString comment ; - QList icons; - - GxsIdDetails::MakeIdDesc(mPosts[i].mAuthorId, false,s, icons, comment,GxsIdDetails::ICON_TYPE_NONE); - } - break; - } - - if(!strings.empty()) - { - mPosts[i].mPostFlags &= ~(ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER); - - for(auto iter(strings.begin()); iter != strings.end(); ++iter) - if(s.contains(*iter,Qt::CaseInsensitive)) - { - mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; - - count++; - break; - } - } - else - { - mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER |ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; - count++; - } - - for(uint32_t j=0;j 0) - mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; - } - - return count; -} - - -void RsGxsForumModel::setFilter(int column,const QStringList& strings,uint32_t& count) -{ - preMods(); - - if(!strings.empty()) - { - count = recursUpdateFilterStatus(ForumModelIndex(0),column,strings); - mFilteringEnabled = true; - } - else - { - count=0; - mFilteringEnabled = false; - } - - postMods(); -} - -QVariant RsGxsForumModel::missingRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) - return QVariant(true); - else - return QVariant(false); -} - -QVariant RsGxsForumModel::toolTipRole(const ForumModelPostEntry& fmpe,int column) const -{ - if(column == COLUMN_THREAD_DISTRIBUTION) - switch(fmpe.mReputationWarningLevel) - { - case 3: return QVariant(tr("Information for this identity is currently missing.")) ; - case 2: return QVariant(tr("You have banned this ID. The message will not be\ndisplayed nor forwarded to your friends.")) ; - case 1: return QVariant(tr("You have not set an opinion for this person,\n and your friends do not vote positively: Spam regulation \nprevents the message to be forwarded to your friends.")) ; - case 0: return QVariant(tr("Message will be forwarded to your friends.")) ; - default: - return QVariant("[ERROR: missing reputation level information - contact the developers]"); - } - - if(column == COLUMN_THREAD_AUTHOR) - { - QString str,comment ; - QList icons; - - if(!GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) - return QVariant(); - - int S = QFontMetricsF(QApplication::font()).height(); - QImage pix( (*icons.begin()).pixmap(QSize(4*S,4*S)).toImage()); - - QString embeddedImage; - if(RsHtml::makeEmbeddedImage(pix.scaled(QSize(4*S,4*S), Qt::KeepAspectRatio, Qt::SmoothTransformation), embeddedImage, 8*S * 8*S)) - comment = "
" + embeddedImage + "" + comment + "
"; - - return comment; - } - - return QVariant(); -} - -QVariant RsGxsForumModel::pinnedRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) - return QVariant(true); - else - return QVariant(false); -} - -QVariant RsGxsForumModel::backgroundRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) - return QVariant(QBrush(QColor(255,200,180))); - - if(mFilteringEnabled && (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_PASSES_FILTER)) - return QVariant(QBrush(QColor(255,240,210))); - - return QVariant(); -} -#endif - QVariant RsGxsChannelPostsModel::sizeHintRole(int col) const { float factor = QFontMetricsF(QApplication::font()).height()/14.0f ; return QVariant( QSize(factor * 170, factor*14 )); -#ifdef TODO - switch(col) - { - default: - case COLUMN_THREAD_TITLE: return QVariant( QSize(factor * 170, factor*14 )); - case COLUMN_THREAD_DATE: return QVariant( QSize(factor * 75 , factor*14 )); - case COLUMN_THREAD_AUTHOR: return QVariant( QSize(factor * 75 , factor*14 )); - case COLUMN_THREAD_DISTRIBUTION: return QVariant( QSize(factor * 15 , factor*14 )); - } -#endif } -#ifdef TODO -QVariant RsGxsForumModel::authorRole(const ForumModelPostEntry& fmpe,int column) const -{ - if(column == COLUMN_THREAD_DATA) - return QVariant(QString::fromStdString(fmpe.mAuthorId.toStdString())); - - return QVariant(); -} - -QVariant RsGxsForumModel::sortRole(const ForumModelPostEntry& fmpe,int column) const -{ - switch(column) - { - case COLUMN_THREAD_DATE: if(mSortMode == SORT_MODE_PUBLISH_TS) - return QVariant(QString::number(fmpe.mPublishTs)); // we should probably have leading zeroes here - else - return QVariant(QString::number(fmpe.mMostRecentTsInThread)); // we should probably have leading zeroes here - - case COLUMN_THREAD_READ: return QVariant((bool)IS_MSG_UNREAD(fmpe.mMsgStatus)); - case COLUMN_THREAD_DISTRIBUTION: return decorationRole(fmpe,column); - case COLUMN_THREAD_AUTHOR: - { - QString str,comment ; - QList icons; - GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, false, str, icons, comment,GxsIdDetails::ICON_TYPE_NONE); - - return QVariant(str); - } - default: - return displayRole(fmpe,column); - } -} -#endif - QVariant RsGxsChannelPostsModel::displayRole(const RsGxsChannelPost& fmpe,int col) const { switch(col) { default: return QString::fromUtf8(fmpe.mMeta.mMsgName.c_str()); -#ifdef TODO - case COLUMN_THREAD_TITLE: if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_REDACTED) - return QVariant(tr("[ ... Redacted message ... ]")); - else if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) - return QVariant(tr("[PINNED] ") + QString::fromUtf8(fmpe.mTitle.c_str())); - else - return QVariant(QString::fromUtf8(fmpe.mTitle.c_str())); - - case COLUMN_THREAD_READ:return QVariant(); - case COLUMN_THREAD_DATE:{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) - return QVariant(QString()); - - QDateTime qtime; - qtime.setTime_t(fmpe.mPublishTs); - - return QVariant(DateTime::formatDateTime(qtime)); - } - - case COLUMN_THREAD_DISTRIBUTION: - case COLUMN_THREAD_AUTHOR:{ - QString name; - RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString()); - - if(id.isNull()) - return QVariant(tr("[Notification]")); - if(GxsIdTreeItemDelegate::computeName(id,name)) - return name; - return QVariant(tr("[Unknown]")); - } - case COLUMN_THREAD_MSGID: return QVariant(); -#endif -#ifdef TODO - if (filterColumn == COLUMN_THREAD_CONTENT) { - // need content for filter - QTextDocument doc; - doc.setHtml(QString::fromUtf8(msg.mMsg.c_str())); - item->setText(COLUMN_THREAD_CONTENT, doc.toPlainText().replace(QString("\n"), QString(" "))); - } -#endif } @@ -640,23 +333,6 @@ QVariant RsGxsChannelPostsModel::userRole(const RsGxsChannelPost& fmpe,int col) } } -#ifdef TODO -QVariant RsGxsForumModel::decorationRole(const ForumModelPostEntry& fmpe,int col) const -{ - bool exist=false; - switch(col) - { - case COLUMN_THREAD_DISTRIBUTION: - return QVariant(fmpe.mReputationWarningLevel); - case COLUMN_THREAD_READ: - return QVariant(fmpe.mMsgStatus); - case COLUMN_THREAD_AUTHOR://Return icon as place holder. - return FilesDefs::getIconFromGxsIdCache(RsGxsId(fmpe.mAuthorId.toStdString()),QIcon(), exist); - } - return QVariant(); -} -#endif - const RsGxsGroupId& RsGxsChannelPostsModel::currentGroupId() const { return mChannelGroup.mMeta.mGroupId; @@ -766,39 +442,13 @@ void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id) }); } -//ChannelPostsModelIndex RsGxsChannelPostsModel::addEntry(std::vector& posts,const ChannelPostsModelPostEntry& entry) -//{ -// uint32_t N = posts.size(); -// posts.push_back(entry); -// -//#ifdef DEBUG_FORUMMODEL -// std::cerr << "Added new entry " << N << " children of " << parent << std::endl; -//#endif -// if(N == parent) -// std::cerr << "(EE) trying to add a post as its own parent!" << std::endl; -// -// return ChannelPostsModelIndex(N); -//} - -//void RsGxsChannelPostsModel::convertMsgToPostEntry(const RsGxsChannelGroup& mChannelGroup,const RsMsgMetaData& msg, bool /*useChildTS*/, ChannelPostsModelPostEntry& fentry) -//{ -// fentry.mTitle = msg.mMsgName; -// fentry.mMsgId = msg.mMsgId; -// fentry.mPublishTs = msg.mPublishTs; -// fentry.mPostFlags = 0; -// fentry.mMsgStatus = msg.mMsgStatus; -// -// // Early check for a message that should be hidden because its author -// // is flagged with a bad reputation -//} - static bool decreasing_time_comp(const std::pair& e1,const std::pair& e2) { return e2.first < e1.first ; } void RsGxsChannelPostsModel::createPostsArray(std::vector& posts) { // collect new versions of posts if any -#ifndef DEBUG_CHANNEL_MODEL +#ifdef DEBUG_CHANNEL_MODEL std::cerr << "Inserting channel posts" << std::endl; #endif @@ -808,7 +458,7 @@ void RsGxsChannelPostsModel::createPostsArray(std::vector& pos if(posts[i].mMeta.mOrigMsgId == posts[i].mMeta.mMsgId) posts[i].mMeta.mOrigMsgId.clear(); -#ifndef DEBUG_CHANNEL_MODEL +#ifdef DEBUG_CHANNEL_MODEL std::cerr << " " << i << ": name=\"" << posts[i].mMeta.mMsgName << "\" msg_id=" << posts[i].mMeta.mMsgId << ": orig msg id = " << posts[i].mMeta.mOrigMsgId << std::endl; #endif @@ -900,7 +550,7 @@ void RsGxsChannelPostsModel::createPostsArray(std::vector& pos { if(!(*it).mMeta.mMsgId.isNull()) { -#ifndef DEBUG_CHANNEL_MODEL +#ifdef DEBUG_CHANNEL_MODEL std::cerr << " adding post \"" << (*it).mMeta.mMsgName << "\"" << std::endl; #endif mPosts.push_back(*it); @@ -929,77 +579,8 @@ void RsGxsChannelPostsModel::setMsgReadStatus(const QModelIndex& i,bool read_sta // bool has_unread_below, has_read_below; // recursSetMsgReadStatus(entry,read_status,with_children) ; // recursUpdateReadStatusAndTimes(0,has_unread_below,has_read_below); - } -#ifdef TODO -void RsGxsForumModel::recursSetMsgReadStatus(ForumModelIndex i,bool read_status,bool with_children) -{ - int newStatus = (read_status ? mPosts[i].mMsgStatus & ~static_cast(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD) - : mPosts[i].mMsgStatus | static_cast(GXS_SERV::GXS_MSG_STATUS_GUI_UNREAD)); - bool bChanged = (mPosts[i].mMsgStatus != newStatus); - mPosts[i].mMsgStatus = newStatus; - //Remove Unprocessed and New flags - mPosts[i].mMsgStatus &= ~(GXS_SERV::GXS_MSG_STATUS_UNPROCESSED | GXS_SERV::GXS_MSG_STATUS_GUI_NEW); - - if (bChanged) - { - //Don't recurs post versions as this should be done before, if no change. - uint32_t token; - auto s = getPostVersions(mPosts[i].mMsgId) ; - - if(!s.empty()) - for(auto it(s.begin());it!=s.end();++it) - { - rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, it->second ), read_status); - std::cerr << "Setting version " << it->second << " of post " << mPosts[i].mMsgId << " as read." << std::endl; - } - else - rsGxsForums->setMessageReadStatus(token,std::make_pair( mForumGroup.mMeta.mGroupId, mPosts[i].mMsgId ), read_status); - - QModelIndex itemIndex = createIndex(i - 1, 0, &mPosts[i]); - emit dataChanged(itemIndex, itemIndex); - } - - if(!with_children) - return; - - for(uint32_t j=0;j& entries,ForumModelIndex index,int depth) -{ - const ForumModelPostEntry& e(entries[index]); - - QDateTime qtime; - qtime.setTime_t(e.mPublishTs); - - std::cerr << std::string(depth*2,' ') << index << " : " << e.mAuthorId.toStdString() << " " - << QString("%1").arg((uint32_t)e.mPostFlags,8,16,QChar('0')).toStdString() << " " - << QString("%1").arg((uint32_t)e.mMsgStatus,8,16,QChar('0')).toStdString() << " " - << qtime.toString().toStdString() << " \"" << e.mTitle << "\"" << std::endl; - - for(uint32_t i=0;i= mPosts.size()) - return ; - - std::cerr << "Setting own opinion for author " << mPosts[entry].mAuthorId - << " to " << static_cast(op) << std::endl; - RsGxsId author_id = mPosts[entry].mAuthorId; - - rsReputations->setOwnOpinion(author_id,op) ; - - // update opinions and distribution flags. No need to re-load all posts. - - for(uint32_t i=0;ichannelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); + ui->channelPostFiles_TV->setAutoSelect(true); } void GxsChannelPostsWidgetWithModel::updateGroupData() From 591ffc2fa7ef8004d3d882baed2b6e589d9efbb5 Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 6 Jun 2020 22:37:44 +0200 Subject: [PATCH 15/89] added display of selected item and empty widget for channel files --- .../GxsChannelPostsWidgetWithModel.cpp | 318 +----------------- .../GxsChannelPostsWidgetWithModel.ui | 10 + 2 files changed, 19 insertions(+), 309 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 165537823..4a64024a1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -126,66 +126,11 @@ public: void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { -#ifdef TODO - QString byteUnits[4] = {tr("B"), tr("KB"), tr("MB"), tr("GB")}; - QStyleOptionViewItem opt = option; - QStyleOptionProgressBarV2 newopt; - QRect pixmapRect; - QPixmap pixmap; - qlonglong fileSize; - double dlspeed, multi; - int seconds,minutes, hours, days; - qlonglong remaining; - QString temp ; - qlonglong completed; - qlonglong downloadtime; - qint64 qi64Value; -#endif - // prepare painter->save(); painter->setClipRect(option.rect); RsGxsChannelPost post = index.data(Qt::UserRole).value() ; - //const RsGxsChannelPost& post(*pinfo); - -#ifdef TODO - QVariant value = index.data(Qt::TextColorRole); - - if(value.isValid() && qvariant_cast(value).isValid()) - opt.palette.setColor(QPalette::Text, qvariant_cast(value)); - - QPalette::ColorGroup cg = option.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; - - if(option.state & QStyle::State_Selected) - painter->setPen(opt.palette.color(cg, QPalette::HighlightedText)); - else - painter->setPen(opt.palette.color(cg, QPalette::Text)); - - //painter->drawText(option.rect, Qt::AlignRight, QString("TODO")); - - QPixmap thumbnail; - GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); - - QFontMetricsF fm(opt.font); - - int W = IMAGE_SIZE_FACTOR_W * fm.height() * IMAGE_ZOOM_FACTOR; - int H = IMAGE_SIZE_FACTOR_H * fm.height() * IMAGE_ZOOM_FACTOR; - - int w = fm.width("X") ; - int h = fm.height() ; - - float img_coord_x = IMAGE_MARGIN_FACTOR*0.5*w; - float img_coord_y = IMAGE_MARGIN_FACTOR*0.5*h; - - QPoint img_pos(img_coord_x,img_coord_y); - QPoint img_size(W,H); - - QPoint txt_pos(0,img_coord_y + H + h); - - painter->drawPixmap(QRect(opt.rect.topLeft() + img_pos,opt.rect.topLeft()+img_pos+img_size),thumbnail.scaled(W,H,Qt::KeepAspectRatio,Qt::SmoothTransformation)); - painter->drawText(QRect(option.rect.topLeft() + txt_pos,option.rect.bottomRight()),Qt::AlignCenter,QString::fromStdString(post.mMeta.mMsgName)); -#endif painter->save(); painter->fillRect( option.rect, option.backgroundBrush); @@ -193,10 +138,13 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & ThumbnailView w(post); - //w.setFixedWidth(option.rect.width()); - QPixmap pixmap(w.size()); - pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background + + if(option.state & QStyle::State_Selected) + pixmap.fill(QRgb(0xff308dc7)); // I dont know how to grab the backgroud color for selected objects automatically. + else + pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background + w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background // debug @@ -220,21 +168,6 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM QFontMetricsF fm(option.font); return QSize(COLUMN_SIZE_FONT_FACTOR_W*fm.height(),COLUMN_SIZE_FONT_FACTOR_H*fm.height()); - -#ifdef TODO - //QPixmap thumbnail; - //GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); - - QFontMetricsF fm(option.font); - - int W = IMAGE_SIZE_FACTOR_W * fm.height() * IMAGE_ZOOM_FACTOR; - int H = IMAGE_SIZE_FACTOR_H * fm.height() * IMAGE_ZOOM_FACTOR; - - int h = fm.height() ; - int w = fm.width("X") ; - - return QSize(W+IMAGE_MARGIN_FACTOR*w,H + 2*h); -#endif } QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const @@ -255,24 +188,6 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI { RsGxsFile file = index.data(Qt::UserRole).value() ; -#ifdef TODO - - QString byteUnits[4] = {tr("B"), tr("KB"), tr("MB"), tr("GB")}; - - QStyleOptionViewItem opt = option; - QStyleOptionProgressBarV2 newopt; - QRect pixmapRect; - QPixmap pixmap; - qlonglong fileSize; - double dlspeed, multi; - int seconds,minutes, hours, days; - qlonglong remaining; - QString temp ; - qlonglong completed; - qlonglong downloadtime; - qint64 qi64Value; -#endif - // prepare painter->save(); painter->setClipRect(option.rect); @@ -283,21 +198,6 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI //optionFocusRect.backgroundColor = option.palette.color(colorgroup, (option.state & QStyle::State_Selected) ? QPalette::Highlight : QPalette::Background); painter->restore(); -#ifdef TODO - RsGxsFile file = index.data(Qt::UserRole).value() ; - QVariant value = index.data(Qt::TextColorRole); - - if(value.isValid() && qvariant_cast(value).isValid()) - opt.palette.setColor(QPalette::Text, qvariant_cast(value)); - - QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled) ? QPalette::Normal : QPalette::Disabled; - - if(option.state & QStyle::State_Selected) - painter->setPen(option.palette.color(cg, QPalette::HighlightedText)); - else - painter->setPen(option.palette.color(cg, QPalette::Text)); -#endif - switch(index.column()) { case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter," " + QString::fromUtf8(file.mName.c_str())); @@ -315,12 +215,6 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background painter->drawPixmap(option.rect.topLeft(),pixmap); - -#ifdef TODO - FileInfo finfo; - if(rsFiles->FileDetails(file.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) - painter->drawText(option.rect,Qt::AlignLeft,QString::number(finfo.transfered)); -#endif } break; @@ -360,6 +254,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); ui->channelPostFiles_TV->setPlaceholderText(tr("Post files")); + ui->channelFiles_TV->setPlaceholderText(tr("All files in the channel")); + connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); QFontMetricsF fm(font()); @@ -547,6 +443,7 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() { mGroup = groups[0]; mChannelPostsModel->updateChannel(groupId()); + insertChannelDetails(mGroup); } ); }); @@ -974,155 +871,6 @@ void GxsChannelPostsWidget::fillThreadCreatePost(const QVariant &post, bool rela createPostItem(post.value(), related); } - -void GxsChannelPostsWidget::insertChannelPosts(std::vector& posts, GxsMessageFramePostThread *thread, bool related) -{ - if (related && thread) { - std::cerr << "GxsChannelPostsWidget::insertChannelPosts fill only related posts as thread is not possible" << std::endl; - return; - } - - int count = posts.size(); - int pos = 0; - - if (!thread) { - ui->feedWidget->setSortingEnabled(false); - } - - // collect new versions of posts if any - -#ifdef DEBUG_CHANNEL - std::cerr << "Inserting channel posts" << std::endl; -#endif - - std::vector new_versions ; - for (uint32_t i=0;i search_map ; - for (uint32_t i=0;i versions ; - std::map::const_iterator vit ; - - while(search_map.end() != (vit=search_map.find(posts[current_index].mMeta.mOrigMsgId))) - { -#ifdef DEBUG_CHANNEL - std::cerr << " post at index " << current_index << " replaces a post at position " << vit->second ; -#endif - - // Now replace the post only if the new versionis more recent. It may happen indeed that the same post has been corrected multiple - // times. In this case, we only need to replace the post with the newest version - - //uint32_t prev_index = current_index ; - current_index = vit->second ; - - if(posts[current_index].mMeta.mMsgId.isNull()) // This handles the branching situation where this post has been already erased. No need to go down further. - { -#ifdef DEBUG_CHANNEL - std::cerr << " already erased. Stopping." << std::endl; -#endif - break ; - } - - if(posts[current_index].mMeta.mPublishTs < posts[source_index].mMeta.mPublishTs) - { -#ifdef DEBUG_CHANNEL - std::cerr << " and is more recent => following" << std::endl; -#endif - for(std::set::const_iterator itt(posts[current_index].mOlderVersions.begin());itt!=posts[current_index].mOlderVersions.end();++itt) - posts[source_index].mOlderVersions.insert(*itt); - - posts[source_index].mOlderVersions.insert(posts[current_index].mMeta.mMsgId); - posts[current_index].mMeta.mMsgId.clear(); // clear the msg Id so the post will be ignored - } -#ifdef DEBUG_CHANNEL - else - std::cerr << " but is older -> Stopping" << std::endl; -#endif - } - } - } - -#ifdef DEBUG_CHANNEL - std::cerr << "Now adding posts..." << std::endl; -#endif - - for (std::vector::const_reverse_iterator it = posts.rbegin(); it != posts.rend(); ++it) - { -#ifdef DEBUG_CHANNEL - std::cerr << " adding post: " << (*it).mMeta.mMsgId ; -#endif - - if(!(*it).mMeta.mMsgId.isNull()) - { -#ifdef DEBUG_CHANNEL - std::cerr << " added" << std::endl; -#endif - - if (thread && thread->stopped()) - break; - - if (thread) - thread->emitAddPost(QVariant::fromValue(*it), related, ++pos, count); - else - createPostItem(*it, related); - } -#ifdef DEBUG_CHANNEL - else - std::cerr << " skipped" << std::endl; -#endif - } - - if (!thread) { - ui->feedWidget->setSortingEnabled(true); - } -} - -void GxsChannelPostsWidget::clearPosts() -{ - ui->feedWidget->clear(); - ui->fileWidget->clear(); -} #endif void GxsChannelPostsWidgetWithModel::blank() @@ -1183,57 +931,9 @@ void GxsChannelPostsWidgetWithModel::toggleAutoDownload() << "for channel: " << grpId.toStdString() << std::endl; return; } - -// RsQThreadUtils::postToObject( [=]() -// { -// /* Here it goes any code you want to be executed on the Qt Gui -// * thread, for example to update the data model with new information -// * after a blocking call to RetroShare API complete, note that -// * Qt::QueuedConnection is important! -// */ -// -// std::cerr << __PRETTY_FUNCTION__ << " Has been executed on GUI " -// << "thread but was scheduled by async thread" << std::endl; -// }, this ); }); } -#ifdef TODO -bool GxsChannelPostsWidgetWithModel::insertGroupData(const RsGxsGenericGroupData *data) -{ - const RsGxsChannelGroup *d = dynamic_cast(data); - - if(!d) - { - RsErr() << __PRETTY_FUNCTION__ << " Cannot dynamic cast input data (" << (void*)data << " to RsGxsGenericGroupData. Something bad happenned." << std::endl; - return false; - } - - insertChannelDetails(*d); - return true; -} - -void GxsChannelPostsWidget::insertAllPosts(const std::vector& posts, GxsMessageFramePostThread *thread) -{ - std::vector cposts; - - for(auto post: posts) // This is not so nice but we have somehow to convert to RsGxsChannelPost at some time, and the cposts list is being modified in the insert method. - cposts.push_back(*static_cast(post)); - - insertChannelPosts(cposts, thread, false); -} - -void GxsChannelPostsWidget::insertPosts(const std::vector& posts) -{ - std::vector cposts; - - for(auto post: posts) // This is not so nice but we have somehow to convert to RsGxsChannelPost at some timer, and the cposts list is being modified in the insert method. - cposts.push_back(*static_cast(post)); - - insertChannelPosts(cposts); -} -#endif - class GxsChannelPostsReadData { public: diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 0f3405cda..e6795a63b 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -536,6 +536,16 @@ p, li { white-space: pre-wrap; }
+ + + Files + + + + + + +
From 944c5b3592bcb0f976bae77d79f8330f97ebe540 Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 7 Jun 2020 14:50:31 +0200 Subject: [PATCH 16/89] added list of files for the whole channel --- .../gxschannels/GxsChannelPostFilesModel.cpp | 35 ++++++------------- .../gxschannels/GxsChannelPostFilesModel.h | 10 ++---- .../gui/gxschannels/GxsChannelPostsModel.cpp | 16 +++++++++ .../gui/gxschannels/GxsChannelPostsModel.h | 5 +++ .../GxsChannelPostsWidgetWithModel.cpp | 17 +++++++++ .../GxsChannelPostsWidgetWithModel.h | 2 ++ .../GxsChannelPostsWidgetWithModel.ui | 16 +++++++-- 7 files changed, 67 insertions(+), 34 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 06ba27a0f..b1b7eddbd 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -345,6 +345,7 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co { case Qt::DisplayRole: return displayRole (fmpe,index.column()) ; case Qt::UserRole: return userRole (fmpe,index.column()) ; + case SortRole: return sortRole (fmpe,index.column()) ; #ifdef TODO case Qt::DecorationRole: return decorationRole(fmpe,index.column()) ; case Qt::ToolTipRole: return toolTipRole (fmpe,index.column()) ; @@ -355,7 +356,6 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co case ThreadPinnedRole: return pinnedRole (fmpe,index.column()) ; case MissingRole: return missingRole (fmpe,index.column()) ; case StatusRole: return statusRole (fmpe,index.column()) ; - case SortRole: return sortRole (fmpe,index.column()) ; #endif default: return QVariant(); @@ -542,39 +542,26 @@ QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const #endif } -#ifdef TODO -QVariant RsGxsForumModel::authorRole(const ForumModelPostEntry& fmpe,int column) const -{ - if(column == COLUMN_THREAD_DATA) - return QVariant(QString::fromStdString(fmpe.mAuthorId.toStdString())); - - return QVariant(); -} - -QVariant RsGxsForumModel::sortRole(const ForumModelPostEntry& fmpe,int column) const +QVariant RsGxsChannelPostFilesModel::sortRole(const RsGxsFile& fmpe,int column) const { switch(column) { - case COLUMN_THREAD_DATE: if(mSortMode == SORT_MODE_PUBLISH_TS) - return QVariant(QString::number(fmpe.mPublishTs)); // we should probably have leading zeroes here - else - return QVariant(QString::number(fmpe.mMostRecentTsInThread)); // we should probably have leading zeroes here - - case COLUMN_THREAD_READ: return QVariant((bool)IS_MSG_UNREAD(fmpe.mMsgStatus)); - case COLUMN_THREAD_DISTRIBUTION: return decorationRole(fmpe,column); - case COLUMN_THREAD_AUTHOR: + case COLUMN_FILES_NAME: return QVariant(QString::fromUtf8(fmpe.mName.c_str())); + case COLUMN_FILES_SIZE: return QVariant(qulonglong(fmpe.mSize)); + case COLUMN_FILES_FILE: { - QString str,comment ; - QList icons; - GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, false, str, icons, comment,GxsIdDetails::ICON_TYPE_NONE); + FileInfo finfo; + if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) + return qulonglong(finfo.transfered); - return QVariant(str); + return QVariant(qulonglong(fmpe.mSize)); } + break; + default: return displayRole(fmpe,column); } } -#endif QVariant RsGxsChannelPostFilesModel::displayRole(const RsGxsFile& fmpe,int col) const { diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index 873f179e8..eb214516a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -53,15 +53,9 @@ public: COLUMN_FILES_NB_COLUMNS = 0x03 }; -#ifdef TODO enum Roles{ SortRole = Qt::UserRole+1, - ThreadPinnedRole = Qt::UserRole+2, - MissingRole = Qt::UserRole+3, - StatusRole = Qt::UserRole+4, - UnreadChildrenRole = Qt::UserRole+5, - FilterRole = Qt::UserRole+6, + FilterRole = Qt::UserRole+2, }; -#endif #ifdef TODO enum SortMode{ SORT_MODE_PUBLISH_TS = 0x00, @@ -110,13 +104,13 @@ public: QVariant displayRole (const RsGxsFile& fmpe, int col) const; QVariant toolTipRole (const RsGxsFile& fmpe, int col) const; QVariant userRole (const RsGxsFile& fmpe, int col) const; + QVariant sortRole (const RsGxsFile& fmpe, int col) const; #ifdef TODO QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; QVariant missingRole (const ForumModelPostEntry& fmpe, int col) const; QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant sortRole (const ForumModelPostEntry& fmpe, int col) const; QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const; QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 6fc4325a5..87cd08ca8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -71,6 +71,22 @@ void RsGxsChannelPostsModel::postMods() emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mPosts.size(),mColumns-1,(void*)NULL)); } +void RsGxsChannelPostsModel::getFilesList(std::list& files) +{ + // We use an intermediate map so as to remove duplicates + + std::map files_map; + + for(uint32_t i=1;i& files); + #ifdef TODO void setSortMode(SortMode mode) ; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 4a64024a1..550e97d39 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -255,8 +255,11 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->channelPostFiles_TV->setPlaceholderText(tr("Post files")); ui->channelFiles_TV->setPlaceholderText(tr("All files in the channel")); + ui->channelFiles_TV->setModel(mChannelFilesModel = new RsGxsChannelPostFilesModel()); + ui->channelFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); + connect(mChannelPostsModel,SIGNAL(channelLoaded()),this,SLOT(updateChannelFiles())); QFontMetricsF fm(font()); @@ -416,6 +419,20 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); ui->channelPostFiles_TV->setAutoSelect(true); + +} + +void GxsChannelPostsWidgetWithModel::updateChannelFiles() +{ + std::list files; + + mChannelPostsModel->getFilesList(files); + mChannelFilesModel->setFiles(files); + + ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); + ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); + ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); + ui->channelFiles_TV->setAutoSelect(true); } void GxsChannelPostsWidgetWithModel::updateGroupData() diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 48caeaf49..c679a732d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -136,6 +136,7 @@ private slots: void setViewMode(int viewMode); void settingsChanged(); void handlePostsTreeSizeChange(QSize s); + void updateChannelFiles(); private: void processSettings(bool load); @@ -157,6 +158,7 @@ private: RsGxsChannelPostsModel *mChannelPostsModel; RsGxsChannelPostFilesModel *mChannelPostFilesModel; + RsGxsChannelPostFilesModel *mChannelFilesModel; /* UI - from Designer */ Ui::GxsChannelPostsWidgetWithModel *ui; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index e6795a63b..5cc94fbbc 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -224,7 +224,7 @@ - 1 + 2 @@ -530,6 +530,11 @@ p, li { white-space: pre-wrap; } Comments + + + + + @@ -542,7 +547,14 @@ p, li { white-space: pre-wrap; } - + + + QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked + + + true + + From 1cb6369cb6e0febeb49d4f2141be0077164528cd Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 7 Jun 2020 22:50:17 +0200 Subject: [PATCH 17/89] added comments --- .../src/gui/gxs/GxsCommentDialog.cpp | 22 ++++++++++++++++--- retroshare-gui/src/gui/gxs/GxsCommentDialog.h | 4 ++++ .../GxsChannelPostsWidgetWithModel.cpp | 9 ++++++-- .../GxsChannelPostsWidgetWithModel.ui | 12 +++++++--- 4 files changed, 39 insertions(+), 8 deletions(-) diff --git a/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp b/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp index f9b8b1dbc..6512c0c54 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsCommentDialog.cpp @@ -36,10 +36,12 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic /* Invoke the Qt Designer generated QObject setup routine */ ui->setupUi(this); - //ui->postFrame->setVisible(false); - - ui->treeWidget->setup(token_service, comment_service); + setTokenService(token_service,comment_service); + init(); +} +void GxsCommentDialog::init() +{ /* Set header resize modes and initial section sizes */ QHeaderView * ttheader = ui->treeWidget->header () ; ttheader->resizeSection (0, 440); @@ -62,6 +64,20 @@ GxsCommentDialog::GxsCommentDialog(QWidget *parent, RsTokenService *token_servic ui->sortBox->setIconSize(QSize(S*1.5,S*1.5)); } +void GxsCommentDialog::setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service) +{ + ui->treeWidget->setup(token_service, comment_service); +} + +GxsCommentDialog::GxsCommentDialog(QWidget *parent) + : QWidget(parent), ui(new Ui::GxsCommentDialog) +{ + /* Invoke the Qt Designer generated QObject setup routine */ + ui->setupUi(this); + + init(); +} + GxsCommentDialog::~GxsCommentDialog() { delete(ui); diff --git a/retroshare-gui/src/gui/gxs/GxsCommentDialog.h b/retroshare-gui/src/gui/gxs/GxsCommentDialog.h index 82d2fdbd1..4c506dd21 100644 --- a/retroshare-gui/src/gui/gxs/GxsCommentDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsCommentDialog.h @@ -32,9 +32,11 @@ class GxsCommentDialog: public QWidget Q_OBJECT public: + GxsCommentDialog(QWidget *parent); GxsCommentDialog(QWidget *parent, RsTokenService *token_service, RsGxsCommentService *comment_service); virtual ~GxsCommentDialog(); + void setTokenService(RsTokenService *token_service, RsGxsCommentService *comment_service); void setCommentHeader(QWidget *header); void commentLoad(const RsGxsGroupId &grpId, const std::set &msg_versions, const RsGxsMessageId &most_recent_msgId); @@ -48,6 +50,8 @@ private slots: void sortComments(int); private: + void init(); + RsGxsGroupId mGrpId; RsGxsMessageId mMostRecentMsgId; std::set mMsgVersions; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 550e97d39..82bc63234 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -322,6 +322,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->subscribeToolButton->addSubscribedAction(mAutoDownloadAction); + ui->commentsDialog->setTokenService(rsGxsChannels->getTokenService(),rsGxsChannels); + /* Initialize GUI */ setAutoDownload(false); settingsChanged(); @@ -394,6 +396,11 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() mChannelPostFilesModel->setFiles(post.mFiles); + auto all_msgs_versions(post.mOlderVersions); + all_msgs_versions.insert(post.mMeta.mMsgId); + + ui->commentsDialog->commentLoad(post.mMeta.mGroupId, all_msgs_versions, post.mMeta.mMsgId); + std::cerr << "Showing details about selected index : "<< index.row() << "," << index.column() << std::endl; ui->postDetails_TE->setText(RsHtml().formatText(NULL, QString::fromUtf8(post.mMsg.c_str()), RSHTML_FORMATTEXT_EMBED_SMILEYS | RSHTML_FORMATTEXT_EMBED_LINKS)); @@ -417,7 +424,6 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); - ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); ui->channelPostFiles_TV->setAutoSelect(true); } @@ -431,7 +437,6 @@ void GxsChannelPostsWidgetWithModel::updateChannelFiles() ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); - ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); ui->channelFiles_TV->setAutoSelect(true); } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 5cc94fbbc..7bee202b7 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -224,7 +224,7 @@ - 2 + 1 @@ -448,7 +448,7 @@ p, li { white-space: pre-wrap; }
- 1 + 2 @@ -532,7 +532,7 @@ p, li { white-space: pre-wrap; } - + @@ -593,6 +593,12 @@ p, li { white-space: pre-wrap; } QTreeView
gui/common/RSTreeView.h
+ + GxsCommentDialog + QWidget +
gui/gxs/GxsCommentDialog.h
+ 1 +
From 730a3be2e4c86ccf9fa744b2cf7941bfcf704cbe Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 8 Jun 2020 20:56:59 +0200 Subject: [PATCH 18/89] added filtering for posts --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 80 +++++++++++++------ .../gui/gxschannels/GxsChannelPostsModel.h | 10 ++- .../GxsChannelPostsWidgetWithModel.cpp | 13 +-- .../GxsChannelPostsWidgetWithModel.h | 2 +- 4 files changed, 70 insertions(+), 35 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 87cd08ca8..8800513eb 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -45,15 +45,17 @@ const QString RsGxsChannelPostsModel::FilterString("filtered"); RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent) : QAbstractItemModel(parent), mTreeMode(TREE_MODE_PLAIN), mColumns(6) { - initEmptyHierarchy(mPosts); + initEmptyHierarchy(); } -void RsGxsChannelPostsModel::initEmptyHierarchy(std::vector& posts) +void RsGxsChannelPostsModel::initEmptyHierarchy() { preMods(); - posts.resize(1); // adds a sentinel item - posts[0].mMeta.mMsgName = "Root sentinel post" ; + mPosts.resize(1); // adds a sentinel item + mPosts[0].mMeta.mMsgName = "Root sentinel post" ; + mFilteredPosts.resize(1); + mFilteredPosts[0] = 1; postMods(); } @@ -68,7 +70,7 @@ void RsGxsChannelPostsModel::postMods() { endResetModel(); - emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mPosts.size(),mColumns-1,(void*)NULL)); + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL)); } void RsGxsChannelPostsModel::getFilesList(std::list& files) @@ -87,17 +89,43 @@ void RsGxsChannelPostsModel::getFilesList(std::list& files) files.push_back(it.second); } -void RsGxsChannelPostsModel::setTreeMode(TreeMode mode) +void RsGxsChannelPostsModel::setFilter(const QStringList& strings, uint32_t& count) { - if(mode == mTreeMode) - return; - preMods(); - // We're not removing/adding rows here. We're simply asking for re-draw. + beginRemoveRows(QModelIndex(),0,rowCount()-1); + endRemoveRows(); - mTreeMode = mode; - postMods(); + if(strings.empty()) + { + mFilteredPosts.clear(); + for(int i=0;i 0) return 0; - if(mPosts.empty()) // security. Should never happen. + if(mFilteredPosts.empty()) // security. Should never happen. return 0; if(!parent.isValid()) - return (mPosts.size()-1 + mColumns-1)/mColumns; // mPosts always has an item at 0, so size()>=1, and mColumn>=1 + return (mFilteredPosts.size()-1 + mColumns-1)/mColumns; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; return 0; @@ -128,10 +156,10 @@ bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& quintptr ref = i.internalId(); uint32_t entry = 0; - if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size()) return false ; - fmpe = mPosts[entry]; + fmpe = mPosts[mFilteredPosts[entry]]; return true; @@ -247,7 +275,7 @@ quintptr RsGxsChannelPostsModel::getParentRow(quintptr ref,int& row) const { ChannelPostsModelIndex ref_entry; - if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mPosts.size()) + if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredPosts.size()) return 0 ; if(ref_entry == 0) @@ -302,7 +330,7 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const return QVariant() ; } - if(!convertRefPointerToTabEntry(ref,entry) || entry >= mPosts.size()) + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size()) { #ifdef DEBUG_CHANNEL_MODEL std::cerr << "Bad pointer: " << (void*)ref << std::endl; @@ -310,7 +338,7 @@ QVariant RsGxsChannelPostsModel::data(const QModelIndex &index, int role) const return QVariant() ; } - const RsGxsChannelPost& fmpe(mPosts[entry]); + const RsGxsChannelPost& fmpe(mPosts[mFilteredPosts[entry]]); switch(role) { @@ -367,7 +395,7 @@ void RsGxsChannelPostsModel::clear() preMods(); mPosts.clear(); - initEmptyHierarchy(mPosts); + initEmptyHierarchy(); postMods(); emit channelLoaded(); @@ -385,6 +413,10 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto createPostsArray(posts); + mFilteredPosts.clear(); + for(int i=0;i= mPosts.size()) + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredPosts.size()) return ; #warning TODO @@ -603,11 +635,11 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) RsGxsMessageId postId = mid; - for(uint32_t i=1;i &msgs_array, std::vector &posts, std::map > > &mPostVersions); void createPostsArray(std::vector &posts); void setPosts(const RsGxsChannelGroup& group, std::vector &posts); - void initEmptyHierarchy(std::vector& posts); + void initEmptyHierarchy(); + + std::vector mFilteredPosts; // stores the list of displayes indices due to filtering. + std::vector mPosts ; // store the list of posts updated from rsForums. - std::vector mPosts ; // store the list of posts updated from rsForums. //std::map > > mPostVersions; // stores versions of posts QColor mTextColorRead ; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 82bc63234..bfae93391 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -209,6 +209,7 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI GxsChannelFilesStatusWidget w(file); w.setFixedWidth(option.rect.width()); + w.setFixedHeight(option.rect.height()); QPixmap pixmap(w.size()); pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background @@ -283,7 +284,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->feedWidget, SLOT(setFilterText(QString))); connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->fileWidget, SLOT(setFilterText(QString))); #endif - connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged(int))); + connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged())); + connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged())); /* Initialize view button */ //setViewMode(VIEW_MODE_FEEDS); see processSettings @@ -743,12 +745,11 @@ void GxsChannelPostsWidgetWithModel::setViewMode(int viewMode) #endif } -void GxsChannelPostsWidgetWithModel::filterChanged(int filter) +void GxsChannelPostsWidgetWithModel::filterChanged() { -#ifdef TODO - ui->feedWidget->setFilterType(filter); - ui->fileWidget->setFilterType(filter); -#endif + QStringList ql = ui->filterLineEdit->text().split(' ',QString::SkipEmptyParts); + uint32_t count; + mChannelPostsModel->setFilter(ql,count); } #ifdef TODO diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index c679a732d..a4e70d75e 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -132,7 +132,7 @@ private slots: void createMsg(); void toggleAutoDownload(); void subscribeGroup(bool subscribe); - void filterChanged(int filter); + void filterChanged(); void setViewMode(int viewMode); void settingsChanged(); void handlePostsTreeSizeChange(QSize s); From 405fd2b5cae7bbf2cf0b1b16dccfe3faaf7b1113 Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 8 Jun 2020 21:09:23 +0200 Subject: [PATCH 19/89] removed some unused buttons --- .../GxsChannelPostsWidgetWithModel.cpp | 55 +++++++------- .../GxsChannelPostsWidgetWithModel.h | 2 +- .../GxsChannelPostsWidgetWithModel.ui | 71 +------------------ 3 files changed, 29 insertions(+), 99 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index bfae93391..3aa9084d6 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -277,31 +277,24 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->postButton->setText(tr("Add new post")); /* add filter actions */ - ui->filterLineEdit->addFilter(QIcon(), tr("Title"), FILTER_TITLE, tr("Search Title")); - ui->filterLineEdit->addFilter(QIcon(), tr("Message"), FILTER_MSG, tr("Search Message")); - ui->filterLineEdit->addFilter(QIcon(), tr("Filename"), FILTER_FILE_NAME, tr("Search Filename")); -#ifdef TODO - connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->feedWidget, SLOT(setFilterText(QString))); - connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), ui->fileWidget, SLOT(setFilterText(QString))); -#endif - connect(ui->filterLineEdit, SIGNAL(filterChanged(int)), this, SLOT(filterChanged())); - connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged())); + connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); /* Initialize view button */ //setViewMode(VIEW_MODE_FEEDS); see processSettings //ui->infoWidget->hide(); - QSignalMapper *signalMapper = new QSignalMapper(this); - connect(ui->feedToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); - connect(ui->fileToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); - signalMapper->setMapping(ui->feedToolButton, VIEW_MODE_FEEDS); - signalMapper->setMapping(ui->fileToolButton, VIEW_MODE_FILES); - connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setViewMode(int))); + //QSignalMapper *signalMapper = new QSignalMapper(this); + //connect(ui->feedToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); + //connect(ui->fileToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); + //signalMapper->setMapping(ui->feedToolButton, VIEW_MODE_FEEDS); + //signalMapper->setMapping(ui->fileToolButton, VIEW_MODE_FILES); + //connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setViewMode(int))); /*************** Setup Left Hand Side (List of Channels) ****************/ - ui->loadingLabel->hide(); - ui->progressBar->hide(); + //ui->loadingLabel->hide(); + //ui->progressBar->hide(); + ui->postsTree->setPlaceholderText(tr("Thumbnails")); ui->postsTree->setMinimumWidth(COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()+1); @@ -521,21 +514,25 @@ void GxsChannelPostsWidgetWithModel::processSettings(bool load) Settings->beginGroup(QString("ChannelPostsWidget")); if (load) { +#ifdef TO_REMOVE // load settings /* Filter */ - ui->filterLineEdit->setCurrentFilter(Settings->value("filter", FILTER_TITLE).toInt()); + //ui->filterLineEdit->setCurrentFilter(Settings->value("filter", FILTER_TITLE).toInt()); /* View mode */ - setViewMode(Settings->value("viewMode", VIEW_MODE_FEEDS).toInt()); + //setViewMode(Settings->value("viewMode", VIEW_MODE_FEEDS).toInt()); +#endif } else { +#ifdef TO_REMOVE // save settings /* Filter */ - Settings->setValue("filter", ui->filterLineEdit->currentFilter()); + //Settings->setValue("filter", ui->filterLineEdit->currentFilter()); /* View mode */ - Settings->setValue("viewMode", viewMode()); + //Settings->setValue("viewMode", viewMode()); +#endif } Settings->endGroup(); @@ -629,8 +626,8 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou if (IS_GROUP_SUBSCRIBED(group.mMeta.mSubscribeFlags)) { ui->subscribeToolButton->setText(tr("Subscribed") + " " + QString::number(group.mMeta.mPop) ); - ui->feedToolButton->setEnabled(true); - ui->fileToolButton->setEnabled(true); + //ui->feedToolButton->setEnabled(true); + //ui->fileToolButton->setEnabled(true); ui->channel_TW->setTabEnabled(CHANNEL_TABS_POSTS,true); ui->details_TW->setEnabled(true); } @@ -698,14 +695,15 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou ui->infoWidget->show(); ui->feedWidget->hide(); ui->fileWidget->hide(); -#endif - ui->feedToolButton->setEnabled(false); - ui->fileToolButton->setEnabled(false); + //ui->feedToolButton->setEnabled(false); + //ui->fileToolButton->setEnabled(false); +#endif ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); } +#ifdef TODO int GxsChannelPostsWidgetWithModel::viewMode() { if (ui->feedToolButton->isChecked()) { @@ -717,6 +715,7 @@ int GxsChannelPostsWidgetWithModel::viewMode() /* Default */ return VIEW_MODE_FEEDS; } +#endif void GxsChannelPostsWidgetWithModel::setViewMode(int viewMode) { @@ -745,9 +744,9 @@ void GxsChannelPostsWidgetWithModel::setViewMode(int viewMode) #endif } -void GxsChannelPostsWidgetWithModel::filterChanged() +void GxsChannelPostsWidgetWithModel::filterChanged(QString s) { - QStringList ql = ui->filterLineEdit->text().split(' ',QString::SkipEmptyParts); + QStringList ql = s.split(' ',QString::SkipEmptyParts); uint32_t count; mChannelPostsModel->setFilter(ql,count); } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index a4e70d75e..8dd9a66bd 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -132,7 +132,7 @@ private slots: void createMsg(); void toggleAutoDownload(); void subscribeGroup(bool subscribe); - void filterChanged(); + void filterChanged(QString); void setViewMode(int viewMode); void settingsChanged(); void handlePostsTreeSizeChange(QSize s); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 7bee202b7..84a73fb9c 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -120,78 +120,15 @@ - - - - Loading - - - - - - - - 0 - 0 - - - - - 16777215 - 10 - - - - 1000 - - - 0 - - - 0 - - - - Show feeds - - - - :/images/view-feeds.png:/images/view-feeds.png - - - true - - - true - - - - - - - Show files - - - - :/images/view-files.png:/images/view-files.png - - - true - - - true - - - - + 0 @@ -583,11 +520,6 @@ p, li { white-space: pre-wrap; } QToolButton
gui/common/SubscribeToolButton.h
- - LineEditClear - QLineEdit -
gui/common/LineEditClear.h
-
RSTreeView QTreeView @@ -601,7 +533,6 @@ p, li { white-space: pre-wrap; } - From 32050af8da471d6b773c9ade456a468abac0a87c Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 8 Jun 2020 21:26:48 +0200 Subject: [PATCH 20/89] fixed bug causing impossibility to display thumbnails with one post only --- .../src/gui/gxschannels/GxsChannelPostsModel.cpp | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 8800513eb..b2363b99c 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -52,10 +52,12 @@ void RsGxsChannelPostsModel::initEmptyHierarchy() { preMods(); - mPosts.resize(1); // adds a sentinel item - mPosts[0].mMeta.mMsgName = "Root sentinel post" ; - mFilteredPosts.resize(1); - mFilteredPosts[0] = 1; + mPosts.clear(); + mFilteredPosts.clear(); +// mPosts.resize(1); // adds a sentinel item +// mPosts[0].mMeta.mMsgName = "Root sentinel post" ; +// mFilteredPosts.resize(1); +// mFilteredPosts[0] = 1; postMods(); } @@ -105,9 +107,9 @@ void RsGxsChannelPostsModel::setFilter(const QStringList& strings, uint32_t& cou else { mFilteredPosts.clear(); - mFilteredPosts.push_back(0); + //mFilteredPosts.push_back(0); - for(int i=1;i=1, and mColumn>=1 + return (mFilteredPosts.size() + mColumns-1)/mColumns; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; return 0; From fe591c2bd7d1f08931b54d02e12318d37ecbcd8f Mon Sep 17 00:00:00 2001 From: defnax Date: Tue, 9 Jun 2020 00:23:46 +0200 Subject: [PATCH 21/89] fixing stylesheet for the subscribe button --- .../src/gui/qss/stylesheet/Standard.qss | 19 +++++++------------ 1 file changed, 7 insertions(+), 12 deletions(-) diff --git a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss index 53ac80a44..9024c6a83 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss @@ -745,12 +745,7 @@ GxsForumMsgItem QFrame#frame{ background-color: white; } -GxsChannelPostsWidget QFrame#infoFrame -{ - -} - -GxsChannelPostsWidget QToolButton#subscribeToolButton { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton { font: bold; font-size: 14px; color: white; @@ -759,18 +754,18 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton { max-height: 27px; } -GxsChannelPostsWidget QToolButton#subscribeToolButton:hover { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:hover { background: #03b1f3; border-radius: 4px; } -GxsChannelPostsWidget QToolButton#subscribeToolButton:pressed { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:pressed { background: #03b1f3; border-radius: 4px; border: 1px solid gray; } -GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton:disabled { background: gray; border-radius: 4px; border: 1px solid gray; @@ -778,15 +773,15 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton:disabled { } /* only for MenuButtonPopup */ -GxsChannelPostsWidget QToolButton#subscribeToolButton[popupMode="1"] { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton[popupMode="1"] { padding-right: 0px; } -GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-arrow { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-arrow { image: none; } -GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-button { +GxsChannelPostsWidgetWithModel QToolButton#subscribeToolButton::menu-button { image: none; } From dc61167733df4bcfc8fd7ed3c0a32ffca2baa47a Mon Sep 17 00:00:00 2001 From: defnax Date: Tue, 9 Jun 2020 21:03:55 +0200 Subject: [PATCH 22/89] store splitter position --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 3aa9084d6..ab299d343 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -523,6 +523,8 @@ void GxsChannelPostsWidgetWithModel::processSettings(bool load) /* View mode */ //setViewMode(Settings->value("viewMode", VIEW_MODE_FEEDS).toInt()); #endif + // state of splitter + ui->splitter->restoreState(Settings->value("SplitterChannelPosts").toByteArray()); } else { #ifdef TO_REMOVE // save settings @@ -533,6 +535,8 @@ void GxsChannelPostsWidgetWithModel::processSettings(bool load) /* View mode */ //Settings->setValue("viewMode", viewMode()); #endif + // state of splitter + Settings->setValue("SplitterChannelPosts", ui->splitter->saveState()); } Settings->endGroup(); From c9019a7e99ab0d4f07df11840f7e7f749c16c1c6 Mon Sep 17 00:00:00 2001 From: csoler Date: Tue, 9 Jun 2020 21:08:21 +0200 Subject: [PATCH 23/89] added sorting and filtering for files using a QPSortFilterProxyModel but it doesnt work yet --- .../gxschannels/GxsChannelPostFilesModel.cpp | 10 ---- .../gxschannels/GxsChannelPostFilesModel.h | 2 +- .../gui/gxschannels/GxsChannelPostsModel.cpp | 2 - .../gui/gxschannels/GxsChannelPostsModel.h | 2 - .../GxsChannelPostsWidgetWithModel.cpp | 57 ++++++++++++++++++- .../GxsChannelPostsWidgetWithModel.h | 4 ++ .../GxsChannelPostsWidgetWithModel.ui | 2 +- 7 files changed, 62 insertions(+), 17 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index b1b7eddbd..b585d242b 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -384,14 +384,6 @@ QVariant RsGxsForumModel::statusRole(const ForumModelPostEntry& fmpe,int column) return QVariant(fmpe.mMsgStatus); } -QVariant RsGxsForumModel::filterRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(!mFilteringEnabled || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER)) - return QVariant(FilterString); - - return QVariant(QString()); -} - uint32_t RsGxsForumModel::recursUpdateFilterStatus(ForumModelIndex i,int column,const QStringList& strings) { QString s ; @@ -609,8 +601,6 @@ QVariant RsGxsChannelPostFilesModel::displayRole(const RsGxsFile& fmpe,int col) return QVariant(tr("[Unknown]")); } case COLUMN_THREAD_MSGID: return QVariant(); -#endif -#ifdef TODO if (filterColumn == COLUMN_THREAD_CONTENT) { // need content for filter QTextDocument doc; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index eb214516a..e7dd47802 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -105,6 +105,7 @@ public: QVariant toolTipRole (const RsGxsFile& fmpe, int col) const; QVariant userRole (const RsGxsFile& fmpe, int col) const; QVariant sortRole (const RsGxsFile& fmpe, int col) const; + QVariant filterRole (const RsGxsFile& fmpe, int col) const; #ifdef TODO QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; @@ -112,7 +113,6 @@ public: QVariant statusRole (const ForumModelPostEntry& fmpe, int col) const; QVariant authorRole (const ForumModelPostEntry& fmpe, int col) const; QVariant fontRole (const ForumModelPostEntry& fmpe, int col) const; - QVariant filterRole (const ForumModelPostEntry& fmpe, int col) const; QVariant textColorRole (const ForumModelPostEntry& fmpe, int col) const; QVariant backgroundRole(const ForumModelPostEntry& fmpe, int col) const; #endif diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index b2363b99c..94d6f6406 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -40,8 +40,6 @@ Q_DECLARE_METATYPE(RsGxsChannelPost) std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere -const QString RsGxsChannelPostsModel::FilterString("filtered"); - RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent) : QAbstractItemModel(parent), mTreeMode(TREE_MODE_PLAIN), mColumns(6) { diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h index 51d794b4b..b82f91783 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h @@ -101,8 +101,6 @@ public: QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} QModelIndex getIndexOfMessage(const RsGxsMessageId& mid) const; - static const QString FilterString ; - std::vector > getPostVersions(const RsGxsMessageId& mid) const; // This method will asynchroneously update the data diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 3aa9084d6..1d3a47d71 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -240,6 +240,40 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con } } +class RsGxsChannelPostFilesProxyModel: public QSortFilterProxyModel +{ +public: + RsGxsChannelPostFilesProxyModel(QObject *parent = NULL): QSortFilterProxyModel(parent) {} + + bool lessThan(const QModelIndex& left, const QModelIndex& right) const override + { + return left.data(RsGxsChannelPostFilesModel::SortRole) < right.data(RsGxsChannelPostFilesModel::SortRole) ; + } + + bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override + { + if(filter_list.empty()) + return true; + + QString name = sourceModel()->data(sourceModel()->index(source_row,RsGxsChannelPostFilesModel::COLUMN_FILES_NAME,source_parent)).toString(); + + for(auto& s:filter_list) + if(!name.contains(s,Qt::CaseInsensitive)) + return false; + + return true; + } + + void setFilterList(const QStringList& str) + { + filter_list = str; + invalidateFilter(); + } + +private: + QStringList filter_list; +}; + /** Constructor */ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : GxsMessageFrameWidget(rsGxsChannels, parent), @@ -251,14 +285,24 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel()); ui->postsTree->setItemDelegate(new ChannelPostDelegate()); - ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel()); + mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this); + + mChannelPostFilesProxyModel = new RsGxsChannelPostFilesProxyModel(this); + mChannelPostFilesProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); + mChannelPostFilesProxyModel->setSourceModel(mChannelPostFilesModel); + mChannelPostFilesProxyModel->setDynamicSortFilter(true); + + ui->channelPostFiles_TV->setModel(mChannelPostFilesProxyModel); ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); ui->channelPostFiles_TV->setPlaceholderText(tr("Post files")); + ui->channelPostFiles_TV->setSortingEnabled(true); + ui->channelPostFiles_TV->sortByColumn(0, Qt::AscendingOrder); ui->channelFiles_TV->setPlaceholderText(tr("All files in the channel")); ui->channelFiles_TV->setModel(mChannelFilesModel = new RsGxsChannelPostFilesModel()); ui->channelFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); + connect(ui->channelPostFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumn(int,Qt::SortOrder))); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); connect(mChannelPostsModel,SIGNAL(channelLoaded()),this,SLOT(updateChannelFiles())); @@ -277,6 +321,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->postButton->setText(tr("Add new post")); /* add filter actions */ + ui->filterLineEdit->setPlaceholderText(tr("Search...")); connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); /* Initialize view button */ @@ -335,6 +380,12 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize))); } +void GxsChannelPostsWidgetWithModel::sortColumn(int col,Qt::SortOrder so) +{ + std::cerr << "Sorting!!"<< std::endl; + mChannelPostFilesProxyModel->sort(col,so); +} + void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s) { // adjustSize(); @@ -749,6 +800,10 @@ void GxsChannelPostsWidgetWithModel::filterChanged(QString s) QStringList ql = s.split(' ',QString::SkipEmptyParts); uint32_t count; mChannelPostsModel->setFilter(ql,count); + + mChannelPostFilesProxyModel->setFilterKeyColumn(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); + mChannelPostFilesProxyModel->setFilterList(ql); + mChannelPostFilesProxyModel->setFilterRegExp(QRegExp()) ;// triggers a re-display. } #ifdef TODO diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 8dd9a66bd..375290f61 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -34,9 +34,11 @@ class GxsChannelPostsWidgetWithModel; class GxsChannelPostItem; class QTreeWidgetItem; +class QSortFilterProxyModel; class FeedItem; class RsGxsChannelPostsModel; class RsGxsChannelPostFilesModel; +class RsGxsChannelPostFilesProxyModel; class ChannelPostFilesDelegate: public QStyledItemDelegate { @@ -137,6 +139,7 @@ private slots: void settingsChanged(); void handlePostsTreeSizeChange(QSize s); void updateChannelFiles(); + void sortColumn(int col,Qt::SortOrder so); private: void processSettings(bool load); @@ -159,6 +162,7 @@ private: RsGxsChannelPostsModel *mChannelPostsModel; RsGxsChannelPostFilesModel *mChannelPostFilesModel; RsGxsChannelPostFilesModel *mChannelFilesModel; + RsGxsChannelPostFilesProxyModel *mChannelPostFilesProxyModel ; /* UI - from Designer */ Ui::GxsChannelPostsWidgetWithModel *ui; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 84a73fb9c..751c2bc69 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -161,7 +161,7 @@ - 1 + 2 From 2baa5513483a383c50443f9cd2d40c37ebb86f19 Mon Sep 17 00:00:00 2001 From: defnax Date: Tue, 9 Jun 2020 23:32:18 +0200 Subject: [PATCH 24/89] added stretch factor for the splitter --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index ab299d343..d09367cf0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -262,6 +262,10 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); connect(mChannelPostsModel,SIGNAL(channelLoaded()),this,SLOT(updateChannelFiles())); + // Set initial size of the splitter + ui->splitter->setStretchFactor(0, 1); + ui->splitter->setStretchFactor(1, 0); + QFontMetricsF fm(font()); for(int i=0;icolumnCount();++i) From ba6269311d6f91ca54de2a566d4b2abdf98df891 Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 10 Jun 2020 18:24:02 +0200 Subject: [PATCH 25/89] minor changes to files view --- .../GxsChannelPostsWidgetWithModel.cpp | 24 ++++--------------- .../GxsChannelPostsWidgetWithModel.ui | 6 +++++ 2 files changed, 10 insertions(+), 20 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 1d3a47d71..d20d8e5f1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -298,7 +298,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->channelPostFiles_TV->setSortingEnabled(true); ui->channelPostFiles_TV->sortByColumn(0, Qt::AscendingOrder); - ui->channelFiles_TV->setPlaceholderText(tr("All files in the channel")); + ui->channelFiles_TV->setPlaceholderText(tr("No files in the channel, or no channel selected")); ui->channelFiles_TV->setModel(mChannelFilesModel = new RsGxsChannelPostFilesModel()); ui->channelFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); @@ -324,25 +324,11 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->filterLineEdit->setPlaceholderText(tr("Search...")); connect(ui->filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString))); - /* Initialize view button */ - //setViewMode(VIEW_MODE_FEEDS); see processSettings - //ui->infoWidget->hide(); - - //QSignalMapper *signalMapper = new QSignalMapper(this); - //connect(ui->feedToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); - //connect(ui->fileToolButton, SIGNAL(clicked()), signalMapper, SLOT(map())); - //signalMapper->setMapping(ui->feedToolButton, VIEW_MODE_FEEDS); - //signalMapper->setMapping(ui->fileToolButton, VIEW_MODE_FILES); - //connect(signalMapper, SIGNAL(mapped(int)), this, SLOT(setViewMode(int))); - - /*************** Setup Left Hand Side (List of Channels) ****************/ - - //ui->loadingLabel->hide(); - //ui->progressBar->hide(); - ui->postsTree->setPlaceholderText(tr("Thumbnails")); ui->postsTree->setMinimumWidth(COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()+1); + connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize))); + //ui->nameLabel->setMinimumWidth(20); /* Initialize feed widget */ @@ -376,8 +362,6 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI { RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); }, mEventHandlerId, RsEventType::GXS_CHANNELS ); - - connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize))); } void GxsChannelPostsWidgetWithModel::sortColumn(int col,Qt::SortOrder so) @@ -803,7 +787,7 @@ void GxsChannelPostsWidgetWithModel::filterChanged(QString s) mChannelPostFilesProxyModel->setFilterKeyColumn(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); mChannelPostFilesProxyModel->setFilterList(ql); - mChannelPostFilesProxyModel->setFilterRegExp(QRegExp()) ;// triggers a re-display. + mChannelPostFilesProxyModel->setFilterRegExp(s)) ;// triggers a re-display. s is actually not used. } #ifdef TODO diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 751c2bc69..cfa36e40d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -488,9 +488,15 @@ p, li { white-space: pre-wrap; } QAbstractItemView::CurrentChanged|QAbstractItemView::SelectedClicked + + false + true + + false + From 3412bb2a8f9853df5d68bc72f8441d236d57275c Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 10 Jun 2020 18:30:43 +0200 Subject: [PATCH 26/89] fixed compilation --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index d20d8e5f1..846f0bcdd 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -787,7 +787,7 @@ void GxsChannelPostsWidgetWithModel::filterChanged(QString s) mChannelPostFilesProxyModel->setFilterKeyColumn(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); mChannelPostFilesProxyModel->setFilterList(ql); - mChannelPostFilesProxyModel->setFilterRegExp(s)) ;// triggers a re-display. s is actually not used. + mChannelPostFilesProxyModel->setFilterRegExp(s) ;// triggers a re-display. s is actually not used. } #ifdef TODO From cbf4ce66705c8cf6884b32f8415fa4987be32a7c Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 10 Jun 2020 19:05:41 +0200 Subject: [PATCH 27/89] fixed some errors in model --- .../gxschannels/GxsChannelPostFilesModel.cpp | 61 +++++-------------- .../GxsChannelPostsWidgetWithModel.cpp | 2 + 2 files changed, 17 insertions(+), 46 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index b585d242b..8942b65b0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -52,8 +52,7 @@ void RsGxsChannelPostFilesModel::initEmptyHierarchy(std::vector& file { preMods(); - files.resize(1); // adds a sentinel item - files[0].mName = "Root sentinel post" ; + mFiles.clear(); postMods(); } @@ -76,28 +75,6 @@ void RsGxsChannelPostFilesModel::update() emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); } -#ifdef TODO -void RsGxsChannelPostsModel::setSortMode(SortMode mode) -{ - preMods(); - - mSortMode = mode; - - postMods(); -} - -void RsGxsForumModel::initEmptyHierarchy(std::vector& posts) -{ - preMods(); - - posts.resize(1); // adds a sentinel item - posts[0].mTitle = "Root sentinel post" ; - posts[0].mParent = 0; - - postMods(); -} -#endif - int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const { if(parent.column() > 0) @@ -107,7 +84,7 @@ int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const return 0; if(!parent.isValid()) - return getChildrenCount(0); + return (mFiles.size() + COLUMN_FILES_NB_COLUMNS-1)/COLUMN_FILES_NB_COLUMNS; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; return 0; @@ -162,7 +139,7 @@ bool RsGxsChannelPostFilesModel::convertTabEntryToRefPointer(uint32_t entry,quin // This means that the whole software has the following build-in limitation: // * 4 B simultaenous posts. Should be enough ! - ref = (intptr_t)entry; + ref = (intptr_t)(entry+1); return true; } @@ -176,7 +153,12 @@ bool RsGxsChannelPostFilesModel::convertRefPointerToTabEntry(quintptr ref, uint3 RsErr() << "(EE) trying to make a ChannelPostsModelIndex out of a number that is larger than 2^32-1 !" << std::endl; return false ; } - entry = quintptr(val); + if(val==0) + { + RsErr() << "(EE) trying to make a ChannelPostsFileModelIndex out of index 0." << std::endl; + return false; + } + entry = val-1; return true; } @@ -218,15 +200,10 @@ quintptr RsGxsChannelPostFilesModel::getChildRef(quintptr ref,int index) const if (index < 0) return 0; - ChannelPostFilesModelIndex entry ; - - if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) - return 0 ; - - if(entry == 0) + if(ref == quintptr(0)) { quintptr new_ref; - convertTabEntryToRefPointer(index+1,new_ref); + convertTabEntryToRefPointer(index,new_ref); return new_ref; } else @@ -253,20 +230,12 @@ quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const int RsGxsChannelPostFilesModel::getChildrenCount(quintptr ref) const { - uint32_t entry = 0 ; + uint32_t entry = 0 ; - if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) - return 0 ; + if(ref == quintptr(0)) + return rowCount()-1; - if(entry == 0) - { -#ifdef DEBUG_CHANNEL_MODEL - std::cerr << "Children count (flat mode): " << mFiles.size()-1 << std::endl; -#endif - return ((int)mFiles.size())-1; - } - else - return 0; + return 0; } QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation orientation, int role) const diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 846f0bcdd..d6a317151 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -468,6 +468,8 @@ void GxsChannelPostsWidgetWithModel::updateChannelFiles() ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelFiles_TV->setAutoSelect(true); + + mChannelPostFilesProxyModel->sort(0, Qt::AscendingOrder); } void GxsChannelPostsWidgetWithModel::updateGroupData() From 462f52585b0c59fb509cd285f3b96e8ac6a4bea3 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 8 May 2020 16:10:17 +0200 Subject: [PATCH 28/89] add helper for i2p related functions --- libretroshare/src/libretroshare.pro | 4 +- libretroshare/src/util/i2pcommon.cpp | 172 +++++++++++++++++++++ libretroshare/src/util/i2pcommon.h | 222 +++++++++++++++++++++++++++ 3 files changed, 397 insertions(+), 1 deletion(-) create mode 100644 libretroshare/src/util/i2pcommon.cpp create mode 100644 libretroshare/src/util/i2pcommon.h diff --git a/libretroshare/src/libretroshare.pro b/libretroshare/src/libretroshare.pro index 76a39b662..837c1f715 100644 --- a/libretroshare/src/libretroshare.pro +++ b/libretroshare/src/libretroshare.pro @@ -154,6 +154,7 @@ rs_webui { HEADERS += plugins/pluginmanager.h \ plugins/dlfcn_win32.h \ rsitems/rspluginitems.h \ + util/i2pcommon.h \ util/rsinitedptr.h HEADERS += $$PUBLIC_HEADERS @@ -517,7 +518,8 @@ SOURCES += ft/ftchunkmap.cc \ ft/ftfilesearch.cc \ ft/ftserver.cc \ ft/fttransfermodule.cc \ - ft/ftturtlefiletransferitem.cc + ft/ftturtlefiletransferitem.cc \ + util/i2pcommon.cpp SOURCES += crypto/chacha20.cpp \ crypto/hashstream.cc\ diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp new file mode 100644 index 000000000..e50fc9d83 --- /dev/null +++ b/libretroshare/src/util/i2pcommon.cpp @@ -0,0 +1,172 @@ +#include "i2pcommon.h" + +#include "util/rsbase64.h" +#include "util/rsdebug.h" + +namespace i2p { + +const std::string generateNameSuffix(const size_t len) { + std::vector tmp(len); + RsRandom::random_bytes(tmp.data(), len); + const std::string location = Radix32::encode(tmp.data(), len); + + return location; +} + +std::string keyToBase32Addr(const std::string &key) +{ + std::string copy(key); + + // replace I2P specific chars + std::replace(copy.begin(), copy.end(), '~', '/'); + std::replace(copy.begin(), copy.end(), '-', '+'); + + // decode + std::vector bin; + RsBase64::decode(copy, bin); + + // hash + std::vector sha256 = RsUtil::BinToSha256(bin); + // encode + std::string out = Radix32::encode(sha256); + + // i2p uses lowercase + std::transform(out.begin(), out.end(), out.begin(), ::tolower); + out.append(".b32.i2p"); + + return out; +} + +const std::string makeOption(const std::string &lhs, const int8_t &rhs) { + // convert number to int + std::ostringstream oss; + oss << (int)rhs; + return lhs + "=" + oss.str(); +} + +uint16_t readTwoBytesBE(std::vector::const_iterator &p) +{ + uint16_t val = 0; + val += *p++; + val <<= 8; + val += *p++; + return val; +} + +std::string publicKeyFromPrivate(std::string const &priv) +{ + /* + * https://geti2p.net/spec/common-structures#destination + * https://geti2p.net/spec/common-structures#keysandcert + * https://geti2p.net/spec/common-structures#certificate + */ + if (priv.length() < 884) // base64 ( = 663 bytes = KeyCert + priv Keys) + return std::string(); + + // creat a copy to work on, need to convert it to standard base64 + auto priv_copy(priv); + std::replace(priv_copy.begin(), priv_copy.end(), '~', '/'); + std::replace(priv_copy.begin(), priv_copy.end(), '-', '+'); + + // get raw data + std::vector dataPriv; + RsBase64::decode(priv_copy, dataPriv); + + auto p = dataPriv.cbegin(); + RsDbg() << __PRETTY_FUNCTION__ << " dataPriv.size " << dataPriv.size() << std::endl; + + size_t publicKeyLen = 256 + 128; // default length (bytes) + uint8_t certType = 0; + uint16_t len = 0; + uint16_t signingKeyType = 0; + uint16_t cryptKey = 0; + + // only used for easy break + do { + try { + // jump to certificate + p += publicKeyLen; + // try to read type and length + certType = *p++; + len = readTwoBytesBE(p); + + // only 0 and 5 are used / valid at this point + // check for == 0 + if (certType == static_cast::type>(CertType::Null)) { + /* + * CertType.Null + * type null is followed by 0x00 0x00 + * so has to be 0! + */ + RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Null" << std::endl; + publicKeyLen += 3; // add 0x00 0x00 0x00 + + if (len != 0) + // weird + RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Null but len != 0" << std::endl; + + break; + } + + // check for != 5 + if (certType != static_cast::type>(CertType::Key)) { + // unsupported + RsDbg() << __PRETTY_FUNCTION__ << " cert type " << certType << " is unsupported" << std::endl; + return std::string(); + } + + RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Key" << std::endl; + publicKeyLen += 7; // = 1 + 2 + 2 + 2 = 7 bytes + + /* + * "Key certificates were introduced in release 0.9.12. Prior to that release, all PublicKeys were 256-byte ElGamal keys, and all SigningPublicKeys were 128-byte DSA-SHA1 keys." + * --> there is space for 256+128 bytes, longer keys are splitted and appended to the certificate + * We don't need to bother with the splitting here as only the lenght is important! + */ + + // Signing Public Key + // likely 7 + signingKeyType = readTwoBytesBE(p); + + RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << std::endl; + if (signingKeyType >= 3 && signingKeyType <= 6) { + RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " has oversize" << std::endl; + // calculate oversize + + auto it = signingKeyLengths.find(static_cast(signingKeyType)); + if (it == signingKeyLengths.end()) { + // just in case + RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " cannot be found in size map!" << std::endl; + return std::string(); + } + + if (it->second.first <= 128) { + // just in case, it's supposed to be larger! + RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " is oversize but size calculation would underflow!" << std::endl; + return std::string(); + } + + publicKeyLen += it->second.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted + } + + // Crypto Public Key + // likely 0 + cryptKey = readTwoBytesBE(p); + RsDbg() << __PRETTY_FUNCTION__ << " crypto pubkey type " << cryptKey << std::endl; + // info: these are all smaller than the default 256 bytes, so no oversize calculation is needed + + break; + } catch (const std::out_of_range &e) { + RsDbg() << __PRETTY_FUNCTION__ << " hit exception!" << e.what() << std::endl; + return std::string(); + } + } while(false); + + std::string pub; + auto data2 = std::vector(dataPriv.cbegin(), dataPriv.cbegin() + publicKeyLen); + RsBase64::encode(data2.data(), data2.size(), pub, false, false); + + return pub; +} + +} // namespace i2p diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h new file mode 100644 index 000000000..98815496b --- /dev/null +++ b/libretroshare/src/util/i2pcommon.h @@ -0,0 +1,222 @@ +#ifndef I2PCOMMON_H +#define I2PCOMMON_H + +#include +#include + +#include "util/rsrandom.h" +#include "util/radix32.h" +#include "util/rsbase64.h" +#include "util/rsprint.h" +#include "util/rsdebug.h" + +/* + * This header provides common code for i2p related code, namely BOB and SAM3 support. + */ + +namespace i2p { + +static constexpr int8_t kDefaultLength = 3; // i2p default +static constexpr int8_t kDefaultQuantity = 3; // i2p default + 1 +static constexpr int8_t kDefaultVariance = 0; +static constexpr int8_t kDefaultBackupQuantity = 0; + +/** + * @brief The address struct + * This structure is a container for any i2p address/key. The public key is used for addressing and can be (optionally) hashed to generate the .b32.i2p address. + */ +struct address { + std::string base32; + std::string publicKey; + std::string privateKey; + + void clear() { + base32.clear(); + publicKey.clear(); + privateKey.clear(); + } +}; + +/** + * @brief The settings struct + * Common structure with all settings that are shared between any i2p backends + */ +struct settings { + bool enable; + struct address address; + + // connection parameter + int8_t inLength; + int8_t inQuantity; + int8_t inVariance; + int8_t inBackupQuantity; + + int8_t outLength; + int8_t outQuantity; + int8_t outVariance; + int8_t outBackupQuantity; + + void initDefault() { + enable = false; + address.clear(); + + inLength = kDefaultLength; + inQuantity = kDefaultQuantity; + inVariance = kDefaultVariance; + inBackupQuantity = kDefaultBackupQuantity; + + outLength = kDefaultLength; + outQuantity = kDefaultQuantity; + outVariance = kDefaultVariance; + outBackupQuantity = kDefaultBackupQuantity; + } +}; + +/* + Type Type Code Payload Length Total Length Notes + Null 0 0 3 + HashCash 1 varies varies Experimental, unused. Payload contains an ASCII colon-separated hashcash string. + Hidden 2 0 3 Experimental, unused. Hidden routers generally do not announce that they are hidden. + Signed 3 40 or 72 43 or 75 Experimental, unused. Payload contains a 40-byte DSA signature, optionally followed by the 32-byte Hash of the signing Destination. + Multiple 4 varies varies Experimental, unused. Payload contains multiple certificates. + Key 5 4+ 7+ Since 0.9.12. See below for details. +*/ +enum class CertType : uint8_t { + Null = 0, + HashCash = 1, + Hidden = 2, + Signed = 3, + Multiple = 4, + Key = 5 +}; + +/* + * public + Type Type Code Total Public Key Length Since Usage + DSA_SHA1 0 128 0.9.12 Legacy Router Identities and Destinations, never explicitly set + ECDSA_SHA256_P256 1 64 0.9.12 Older Destinations + ECDSA_SHA384_P384 2 96 0.9.12 Rarely if ever used for Destinations + ECDSA_SHA512_P521 3 132 0.9.12 Rarely if ever used for Destinations + RSA_SHA256_2048 4 256 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations + RSA_SHA384_3072 5 384 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations + RSA_SHA512_4096 6 512 0.9.12 Offline only; never used in Key Certificates for Router Identities or Destinations + EdDSA_SHA512_Ed25519 7 32 0.9.15 Recent Router Identities and Destinations + EdDSA_SHA512_Ed25519ph 8 32 0.9.25 Offline only; never used in Key Certificates for Router Identities or Destinations + reserved (GOST) 9 64 Reserved, see proposal 134 + reserved (GOST) 10 128 Reserved, see proposal 134 + RedDSA_SHA512_Ed25519 11 32 0.9.39 For Destinations and encrypted leasesets only; never used for Router Identities + reserved 65280-65534 Reserved for experimental use + reserved 65535 Reserved for future expansion + + * private + Type Length (bytes) Since Usage + DSA_SHA1 20 Legacy Router Identities and Destinations + ECDSA_SHA256_P256 32 0.9.12 Recent Destinations + ECDSA_SHA384_P384 48 0.9.12 Rarely used for Destinations + ECDSA_SHA512_P521 66 0.9.12 Rarely used for Destinations + RSA_SHA256_2048 512 0.9.12 Offline signing, never used for Router Identities or Destinations + RSA_SHA384_3072 768 0.9.12 Offline signing, never used for Router Identities or Destinations + RSA_SHA512_4096 1024 0.9.12 Offline signing, never used for Router Identities or Destinations + EdDSA_SHA512_Ed25519 32 0.9.15 Recent Router Identities and Destinations + EdDSA_SHA512_Ed25519ph 32 0.9.25 Offline signing, never used for Router Identities or Destinations + RedDSA_SHA512_Ed25519 32 0.9.39 For Destinations and encrypted leasesets only, never used for Router Identities + */ +enum class SigningKeyType : uint16_t { + DSA_SHA1 = 0, + ECDSA_SHA256_P256 = 1, + ECDSA_SHA384_P384 = 2, + ECDSA_SHA512_P521 = 3, + RSA_SHA256_2048 = 4, + RSA_SHA384_3072 = 5, + RSA_SHA512_4096 = 6, + EdDSA_SHA512_Ed25519 = 7, + EdDSA_SHA512_Ed25519ph = 8, + RedDSA_SHA512_Ed25519 = 11 +}; + +/* + * public + Type Type Code Total Public Key Length Usage + ElGamal 0 256 All Router Identities and Destinations + P256 1 64 Reserved, see proposal 145 + P384 2 96 Reserved, see proposal 145 + P521 3 132 Reserved, see proposal 145 + X25519 4 32 Not for use in key certs. See proposal 144 + reserved 65280-65534 Reserved for experimental use + reserved 65535 Reserved for future expansion + + * private + Type Length (bytes) Since Usage + ElGamal 256 All Router Identities and Destinations + P256 32 TBD Reserved, see proposal 145 + P384 48 TBD Reserved, see proposal 145 + P521 66 TBD Reserved, see proposal 145 + X25519 32 0.9.38 Little-endian. See proposal 144 +*/ +enum class CryptoKeyType : uint16_t { + ElGamal = 0, + P256 = 1, + P384 = 2, + P521 = 3, + X25519 = 4 +}; + +static const std::map> cryptoKeyLengths { + {CryptoKeyType::ElGamal, {256, 256}}, + {CryptoKeyType::P256, { 64, 32}}, + {CryptoKeyType::P384, { 96, 48}}, + {CryptoKeyType::P521, {132, 66}}, + {CryptoKeyType::X25519, { 32, 32}}, +}; + +static const std::map> signingKeyLengths { + {SigningKeyType::DSA_SHA1, {128, 128}}, + {SigningKeyType::ECDSA_SHA256_P256, { 64, 32}}, + {SigningKeyType::ECDSA_SHA384_P384, { 96, 48}}, + {SigningKeyType::ECDSA_SHA512_P521, {132, 66}}, + {SigningKeyType::RSA_SHA256_2048, {256, 512}}, + {SigningKeyType::RSA_SHA384_3072, {384, 768}}, + {SigningKeyType::RSA_SHA512_4096, {512,1024}}, + {SigningKeyType::EdDSA_SHA512_Ed25519, { 32, 32}}, + {SigningKeyType::EdDSA_SHA512_Ed25519ph,{ 32, 32}}, + {SigningKeyType::RedDSA_SHA512_Ed25519, { 32, 32}}, +}; + + +/** + * @brief generateNameSuffix Generates a base32 name suffix for tunnel identification + * @param len lenght of random bytes, will be expanded by base32 encoding + * @return base32 string + * + * RSRandom::random_alphaNumericString can return very weird looking strings like: ,,@z+M + * -> so use base32 instead + * + * 5 characters = 8 base32 symbols + */ +const std::string generateNameSuffix(const size_t len = 5); + +/** + * @brief makeOption Creates the string "lhs=rhs" used by BOB and SAM. Converts rhs + * @param lhs option to set + * @param rhs value to set + * @return concatenated string + */ +const std::string makeOption(const std::string &lhs, const int8_t &rhs); + +/** + * @brief keyToBase32Addr generated a base32 address (.b32.i2p) from a given public key + * @param key public key + * @return generated base32 address + */ +std::string keyToBase32Addr(const std::string &key); + +/** + * @brief publicKeyFromPrivate parses the private key and calculates the lenght of the public key + * @param priv private key (which includes the public key) to read + * @return public key used for addressing + */ +std::string publicKeyFromPrivate(const std::string &priv); + +} // namespace i2p + +#endif // I2PCOMMON_H From 4b6e12ca454d8673c3f0bdac3f7ff2f890305849 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 8 May 2020 16:13:27 +0200 Subject: [PATCH 29/89] i2p: bob: use common i2p::keyToBase32Addr() --- .../src/services/autoproxy/p3i2pbob.cc | 24 +------------------ .../src/services/autoproxy/p3i2pbob.h | 9 ++++--- 2 files changed, 5 insertions(+), 28 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index 693570ac2..256a1cf4d 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -250,28 +250,6 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) } } -std::string p3I2pBob::keyToBase32Addr(const std::string &key) -{ - std::string copy(key); - - // replace I2P specific chars - std::replace(copy.begin(), copy.end(), '~', '/'); - std::replace(copy.begin(), copy.end(), '-', '+'); - - // decode - std::vector bin = Radix64::decode(copy); - // hash - std::vector sha256 = RsUtil::BinToSha256(bin); - // encode - std::string out = Radix32::encode(sha256); - - // i2p uses lowercase - std::transform(out.begin(), out.end(), out.begin(), ::tolower); - out.append(".b32.i2p"); - - return out; -} - bool inline isAnswerOk(const std::string &answer) { return (answer.compare(0, 2, "OK") == 0); } @@ -346,7 +324,7 @@ int p3I2pBob::stateMachineBOB() switch (mBOBState) { case bsNewkeysN: key = answer.substr(3, answer.length()-3); - mSetting.addr = keyToBase32Addr(key); + mSetting.addr = i2p::keyToBase32Addr(key); IndicateConfigChanged(); break; case bsGetkeys: diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.h b/libretroshare/src/services/autoproxy/p3i2pbob.h index e6ef60bae..428501a4d 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.h +++ b/libretroshare/src/services/autoproxy/p3i2pbob.h @@ -30,9 +30,10 @@ #include #endif +#include "pqi/p3cfgmgr.h" #include "services/autoproxy/rsautoproxymonitor.h" #include "util/rsthreads.h" -#include "pqi/p3cfgmgr.h" +#include "util/i2pcommon.h" /* * This class implements I2P BOB (BASIC OPEN BRIDGE) communication to allow RS @@ -49,7 +50,7 @@ * * Note 3: * BOB needs a unique name as an ID for each tunnel. - * We use 'RetroShare-' + 8 base32 characters. + * We use 'RetroShare-' + 8 random base32 characters. * * Design: * The service uses three state machines to manage its task: @@ -72,7 +73,7 @@ * mCommands[bobState::quit] = {quit, bobState::cleared}; * * stateMachineController: - * This state machone manages the high level tasks. + * This state machine manages the high level tasks. * It is controlled by mState and mTask. * * mTast: @@ -203,8 +204,6 @@ public: void processTaskAsync(taskTicket *ticket); void processTaskSync(taskTicket *ticket); - static std::string keyToBase32Addr(const std::string &key); - void threadTick() override; /// @see RsTickingThread private: From 6136416b5627a6858eace83d78b406c636059e9f Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 8 May 2020 16:33:47 +0200 Subject: [PATCH 30/89] i2p: bob: convert bobSettings to i2p::settings --- libretroshare/src/rsserver/rsinit.cc | 2 +- .../src/services/autoproxy/p3i2pbob.cc | 40 ++++++++----------- .../src/services/autoproxy/p3i2pbob.h | 14 +------ .../src/gui/settings/ServerPage.cpp | 32 ++++++++------- 4 files changed, 35 insertions(+), 53 deletions(-) diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 5dcccfe0c..1c4e2a062 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -1724,7 +1724,7 @@ int RsServer::StartupRetroShare() // now enable bob bobSettings bs; autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::getSettings, &bs); - bs.enableBob = true; + bs.enable = true; autoProxy->taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &bs); } else { std::cerr << "RsServer::StartupRetroShare failed to receive keys" << std::endl; diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index 256a1cf4d..6a52120dc 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -74,15 +74,7 @@ p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr) mProcessing(NULL), mLock("I2P-BOB") { // set defaults - mSetting.enableBob = kDefaultBOBEnable; - mSetting.keys = ""; - mSetting.addr = ""; - mSetting.inLength = kDefaultLength; - mSetting.inQuantity = kDefaultQuantity; - mSetting.inVariance = kDefaultVariance; - mSetting.outLength = kDefaultLength; - mSetting.outQuantity = kDefaultQuantity; - mSetting.outVariance = kDefaultVariance; + mSetting.initDefault(); mCommands.clear(); } @@ -90,7 +82,7 @@ p3I2pBob::p3I2pBob(p3PeerMgr *peerMgr) bool p3I2pBob::isEnabled() { RS_STACK_MUTEX(mLock); - return mSetting.enableBob; + return mSetting.enable; } bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) @@ -151,7 +143,7 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) { RS_STACK_MUTEX(mLock); - addr = mSetting.addr; + addr = mSetting.address.base32; } std::cout << "p3I2pBob::initialSetup addr '" << addr << "'" << std::endl; @@ -324,12 +316,12 @@ int p3I2pBob::stateMachineBOB() switch (mBOBState) { case bsNewkeysN: key = answer.substr(3, answer.length()-3); - mSetting.addr = i2p::keyToBase32Addr(key); + mSetting.address.base32 = i2p::keyToBase32Addr(key); IndicateConfigChanged(); break; case bsGetkeys: key = answer.substr(3, answer.length()-3); - mSetting.keys = key; + mSetting.address.privateKey = key; IndicateConfigChanged(); break; default: @@ -465,7 +457,7 @@ int p3I2pBob::stateMachineController_locked_idle() mProcessing = mPending.front(); mPending.pop(); - if (!mSetting.enableBob && ( + if (!mSetting.enable && ( mProcessing->task == autoProxyTask::start || mProcessing->task == autoProxyTask::stop || mProcessing->task == autoProxyTask::proxyStatusCheck)) { @@ -539,7 +531,7 @@ int p3I2pBob::stateMachineController_locked_connected() switch (mTask) { case ctRunSetUp: // when we have a key use it for server tunnel! - if(mSetting.keys.empty()) { + if(mSetting.address.privateKey.empty()) { rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickC"); mBOBState = bsSetnickC; } else { @@ -750,9 +742,9 @@ bool p3I2pBob::saveList(bool &cleanup, std::list &lst) RsTlvKeyValue kv; RS_STACK_MUTEX(mLock); - addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enableBob ? "TRUE" : "FALSE") - addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.keys) - addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.addr) + addKVS(vitem, kv, kConfigKeyBOBEnable, mSetting.enable ? "TRUE" : "FALSE") + addKVS(vitem, kv, kConfigKeyBOBKey, mSetting.address.privateKey) + addKVS(vitem, kv, kConfigKeyBOBAddr, mSetting.address.base32) addKVSInt(vitem, kv, kConfigKeyInLength, mSetting.inLength) addKVSInt(vitem, kv, kConfigKeyInQuantity, mSetting.inQuantity) addKVSInt(vitem, kv, kConfigKeyInVariance, mSetting.inVariance) @@ -786,11 +778,11 @@ bool p3I2pBob::loadList(std::list &load) RS_STACK_MUTEX(mLock); for(std::list::const_iterator kit = vitem->tlvkvs.pairs.begin(); kit != vitem->tlvkvs.pairs.end(); ++kit) { if (kit->key == kConfigKeyBOBEnable) - mSetting.enableBob = kit->value == "TRUE"; + mSetting.enable = kit->value == "TRUE"; else if (kit->key == kConfigKeyBOBKey) - mSetting.keys = kit->value; + mSetting.address.privateKey = kit->value; else if (kit->key == kConfigKeyBOBAddr) - mSetting.addr = kit->value; + mSetting.address.base32 = kit->value; getKVSUInt(kit, kConfigKeyInLength, mSetting.inLength) getKVSUInt(kit, kConfigKeyInQuantity, mSetting.inQuantity) getKVSUInt(kit, kConfigKeyInVariance, mSetting.inVariance) @@ -958,7 +950,7 @@ void p3I2pBob::finalizeSettings_locked() sockaddr_storage_setport(mI2PProxyAddr, 2827); rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + sockaddr_storage_tostring(mI2PProxyAddr)); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + mSetting.addr); + rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + mSetting.address.base32); peerState ps; mPeerMgr->getOwnNetStatus(ps); @@ -980,7 +972,7 @@ void p3I2pBob::finalizeSettings_locked() const std::string getnick = "getnick RetroShare-" + location; const std::string newkeys = "newkeys"; const std::string getkeys = "getkeys"; - const std::string setkeys = "setkeys " + mSetting.keys; + const std::string setkeys = "setkeys " + mSetting.address.privateKey; const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); @@ -1049,7 +1041,7 @@ void p3I2pBob::updateSettings_locked() peerState ps; mPeerMgr->getOwnNetStatus(ps); - const std::string setkeys = "setkeys " + mSetting.keys; + const std::string setkeys = "setkeys " + mSetting.address.privateKey; const std::string inhost = "inhost " + sockaddr_storage_iptostring(proxy); const std::string inport = toString("inport ", sockaddr_storage_port(proxy)); const std::string outhost = "outhost " + sockaddr_storage_iptostring(ps.localaddr); diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.h b/libretroshare/src/services/autoproxy/p3i2pbob.h index 428501a4d..29bcbcb61 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.h +++ b/libretroshare/src/services/autoproxy/p3i2pbob.h @@ -163,19 +163,7 @@ struct bobStateInfo { bobState nextState; }; -struct bobSettings { - bool enableBob; ///< This field is used by the pqi subsystem to determinine whether SOCKS proxy or BOB is used for I2P connections - std::string keys; ///< (optional) server keys - std::string addr; ///< (optional) hidden service addr. in base32 form - - int8_t inLength; - int8_t inQuantity; - int8_t inVariance; - - int8_t outLength; - int8_t outQuantity; - int8_t outVariance; -}; +struct bobSettings : i2p::settings {}; /// /// \brief The bobStates struct diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index 2881acd60..f0f8566e0 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -23,6 +23,7 @@ #include #include "rshare.h" #include "rsharesettings.h" +#include "util/i2pcommon.h" #include "util/RsNetUtil.h" #include "util/misc.h" @@ -1352,7 +1353,7 @@ void ServerPage::updateInProxyIndicator() ui.iconlabel_service_incoming->setMovie(movie); movie->start(); - if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enableBob) { + if (mHiddenType == RS_HIDDEN_TYPE_I2P && mBobSettings.enable) { QTcpSocket tcpSocket; @@ -1439,15 +1440,16 @@ void ServerPage::getNewKey() void ServerPage::loadKey() { - mBobSettings.keys = ui.pteBobServerKey->toPlainText().toStdString(); - mBobSettings.addr = p3I2pBob::keyToBase32Addr(mBobSettings.keys); + mBobSettings.address.privateKey = ui.pteBobServerKey->toPlainText().toStdString(); + mBobSettings.address.publicKey = i2p::publicKeyFromPrivate(mBobSettings.address.privateKey); + mBobSettings.address.base32 = i2p::keyToBase32Addr(mBobSettings.address.publicKey); rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); } void ServerPage::enableBob(bool checked) { - mBobSettings.enableBob = checked; + mBobSettings.enable = checked; rsAutoProxyMonitor::taskSync(autoProxyType::I2PBOB, autoProxyTask::setSettings, &mBobSettings); @@ -1487,7 +1489,7 @@ void ServerPage::toggleBobAdvancedSettings(bool checked) { ui.swBobAdvanced->setCurrentIndex(checked ? 1 : 0); - if (!mBobSettings.keys.empty()) { + if (!mBobSettings.address.privateKey.empty()) { if (checked) { ui.pbBobGenAddr->show(); } else { @@ -1578,9 +1580,9 @@ void ServerPage::loadCommon() whileBlocking(ui.hiddenpage_proxyPort_i2p_2)->setValue(proxyport); // this one is for bob tab // don't use whileBlocking here - ui.cb_enableBob->setChecked(mBobSettings.enableBob); + ui.cb_enableBob->setChecked(mBobSettings.enable); - if (!mBobSettings.keys.empty()) { + if (!mBobSettings.address.privateKey.empty()) { ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); } @@ -1623,13 +1625,13 @@ void ServerPage::saveBob() void ServerPage::updateStatusBob() { - QString addr = QString::fromStdString(mBobSettings.addr); + QString addr = QString::fromStdString(mBobSettings.address.base32); if (ui.leBobB32Addr->text() != addr) { ui.leBobB32Addr->setText(addr); ui.hiddenpage_serviceAddress->setText(addr); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.keys)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); - if (!mBobSettings.keys.empty()) { + if (!mBobSettings.address.privateKey.empty()) { // we have an addr -> show fields ui.lBobB32Addr->show(); ui.leBobB32Addr->show(); @@ -1655,7 +1657,7 @@ void ServerPage::updateStatusBob() QString bobSimpleText = QString(); bobSimpleText.append(tr("RetroShare uses BOB to set up a %1 tunnel at %2:%3 (named %4)\n\n" "When changing options (e.g. port) use the buttons at the bottom to restart BOB.\n\n"). - arg(mBobSettings.keys.empty() ? tr("client") : tr("server"), + arg(mBobSettings.address.privateKey.empty() ? tr("client") : tr("server"), ui.hiddenpage_proxyAddress_i2p_2->text(), ui.hiddenpage_proxyPort_i2p_2->text(), bs.tunnelName.empty() ? tr("unknown") : @@ -1777,15 +1779,15 @@ void ServerPage::updateStatusBob() void ServerPage::setUpBobElements() { - ui.gbBob->setEnabled(mBobSettings.enableBob); - if (mBobSettings.enableBob) { + ui.gbBob->setEnabled(mBobSettings.enable); + if (mBobSettings.enable) { ui.hiddenpage_proxyAddress_i2p->setEnabled(false); ui.hiddenpage_proxyAddress_i2p->setToolTip("Use I2P/BOB settings to change this value"); ui.hiddenpage_proxyPort_i2p->setEnabled(false); ui.hiddenpage_proxyPort_i2p->setToolTip("Use I2P/BOB settings to change this value"); - ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.addr)); - ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.keys)); + ui.leBobB32Addr->setText(QString::fromStdString(mBobSettings.address.base32)); + ui.pteBobServerKey->setPlainText(QString::fromStdString(mBobSettings.address.privateKey)); // cast to int to avoid problems int li, lo, qi, qo, vi, vo; From c20d92f04cfcb64d508ea25b60bf1e181da61a93 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 8 May 2020 16:50:30 +0200 Subject: [PATCH 31/89] i2p: bob: convert to RsDbg --- .../src/services/autoproxy/p3i2pbob.cc | 113 +++++++++--------- 1 file changed, 57 insertions(+), 56 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index 6a52120dc..a68d85396 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -26,6 +26,7 @@ #include "pqi/p3peermgr.h" #include "rsitems/rsconfigitems.h" +#include "util/rsdebug.h" #include "util/radix32.h" #include "util/radix64.h" #include "util/rsdebug.h" @@ -56,7 +57,7 @@ static const int sleepFactorDefault = 10; // 0.5s static const int sleepFactorFast = 1; // 0.05s static const int sleepFactorSlow = 20; // 1s -static struct RsLog::logInfo i2pBobLogInfo = {RsLog::Default, "p3I2pBob"}; +RS_SET_CONTEXT_DEBUG_LEVEL(0) static const rstime_t selfCheckPeroid = 30; @@ -164,7 +165,7 @@ void p3I2pBob::processTaskAsync(taskTicket *ticket) } break; default: - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::processTaskAsync unknown task"); + RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::processTaskAsync unknown task" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -179,7 +180,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::status: // check if everything needed is set if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::status autoProxyTask::status data is missing"); + RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::status autoProxyTask::status data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -193,7 +194,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::getSettings: // check if everything needed is set if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::getSettings data is missing"); + RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::data_tick autoProxyTask::getSettings data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -207,7 +208,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::setSettings: // check if everything needed is set if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::setSettings data is missing"); + RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::data_tick autoProxyTask::setSettings data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -227,7 +228,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) break; case autoProxyTask::getErrorInfo: if (!data) { - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::data_tick autoProxyTask::getErrorInfo data is missing"); + RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::data_tick autoProxyTask::getErrorInfo data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); } else { RS_STACK_MUTEX(mLock); @@ -236,7 +237,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) } break; default: - rslog(RsLog::Warning, &i2pBobLogInfo, "p3I2pBob::processTaskSync unknown task"); + RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::processTaskSync unknown task" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -257,7 +258,7 @@ void p3I2pBob::threadTick() RS_STACK_MUTEX(mLock); std::stringstream ss; ss << "data_tick mState: " << mState << " mTask: " << mTask << " mBOBState: " << mBOBState << " mPending: " << mPending.size(); - rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str()); + Dbg4() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; } sleepTime += stateMachineController(); @@ -298,13 +299,13 @@ int p3I2pBob::stateMachineBOB() while (answer.find("OK Listing done") == std::string::npos) { std::stringstream ss; ss << "stateMachineBOB status check: read loop, counter: " << counter; - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, ss.str()); + Dbg3() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; answer += recv(); counter++; } if (answer.find(mTunnelName) == std::string::npos) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB status check: tunnel down!"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineBOB status check: tunnel down!" << std::endl; // signal error *((bool *)mProcessing->data) = true; } @@ -344,8 +345,8 @@ int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bo return sleepFactorDefault; } - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB FAILED to run command '" + currentState.command + "'"); - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineBOB '" + answer + "'"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineBOB FAILED to run command '" + currentState.command + "'" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineBOB '" + answer + "'" << std::endl; mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n'); mErrorMsg.append("reason '" + answer + "'" + '\n'); @@ -392,14 +393,14 @@ int p3I2pBob::stateMachineController() return stateMachineController_locked_idle(); case csDoConnect: if (!connectI2P()) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController doConnect: unable to connect"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController doConnect: unable to connect" << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "unable to connect to BOB port"; return sleepFactorSlow; } - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController doConnect: connected"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController doConnect: connected" << std::endl; mState = csConnected; break; case csConnected: @@ -407,7 +408,7 @@ int p3I2pBob::stateMachineController() case csWaitForBob: // check connection problems if (mSocket == 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController waitForBob: conection lost"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController waitForBob: conection lost" << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "connection lost to BOB"; @@ -417,21 +418,21 @@ int p3I2pBob::stateMachineController() // check for finished BOB protocol if (mBOBState == bsCleared) { // done - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController waitForBob: mBOBState == bsCleared"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController waitForBob: mBOBState == bsCleared" << std::endl; mState = csDoDisconnect; } break; case csDoDisconnect: if (!disconnectI2P() || mSocket != 0) { // just in case - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController doDisconnect: can't disconnect"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController doDisconnect: can't disconnect" << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "unable to disconnect from BOB"; return sleepFactorDefault; } - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController doDisconnect: disconnected"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController doDisconnect: disconnected" << std::endl; mState = csDisconnected; break; case csDisconnected: @@ -462,7 +463,7 @@ int p3I2pBob::stateMachineController_locked_idle() mProcessing->task == autoProxyTask::stop || mProcessing->task == autoProxyTask::proxyStatusCheck)) { // skip since we are not enabled - rslog(RsLog::Debug_Alert, &i2pBobLogInfo, "stateMachineController_locked_idle: disabled -> skipping ticket"); + Dbg1() << __PRETTY_FUNCTION__ << " stateMachineController_locked_idle: disabled -> skipping ticket" << std::endl; rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled); mProcessing = NULL; } else { @@ -484,7 +485,7 @@ int p3I2pBob::stateMachineController_locked_idle() mTask = ctRunCheck; break; default: - rslog(RsLog::Debug_Alert, &i2pBobLogInfo, "stateMachineController_locked_idle unknown async task"); + Dbg1() << __PRETTY_FUNCTION__ << " stateMachineController_locked_idle unknown async task" << std::endl; rsAutoProxyMonitor::taskError(mProcessing); mProcessing = NULL; break; @@ -532,28 +533,28 @@ int p3I2pBob::stateMachineController_locked_connected() case ctRunSetUp: // when we have a key use it for server tunnel! if(mSetting.address.privateKey.empty()) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickC"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = setnickC" << std::endl; mBOBState = bsSetnickC; } else { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickS"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = setnickS" << std::endl; mBOBState = bsSetnickS; } break; case ctRunShutDown: // shut down existing tunnel - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = getnick"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = getnick" << std::endl; mBOBState = bsGetnick; break; case ctRunCheck: - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = list"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = list" << std::endl; mBOBState = bsList; break; case ctRunGetKeys: - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_connected: setting mBOBState = setnickN"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = setnickN" << std::endl; mBOBState = bsSetnickN; break; case ctIdle: - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_connected: task is idle. This should not happen!"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: task is idle. This should not happen!" << std::endl; break; } @@ -569,7 +570,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() if(errorHappened) { // reset old state mStateOld = csIdel; - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: error during process!"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: error during process!" << std::endl; } // answer ticket @@ -598,12 +599,12 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; if (!errorHappened) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_disconnected: run check result: ok"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: run check result: ok" << std::endl; break; } // switch to error newState = csError; - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: run check result: error"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: run check result: error" << std::endl; mErrorMsg = "Connection check failed. Will try to restart tunnel."; break; @@ -626,7 +627,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; break; case ctIdle: - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_disconnected: task is idle. This should not happen!"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: task is idle. This should not happen!" << std::endl; rsAutoProxyMonitor::taskError(mProcessing); } mProcessing = NULL; @@ -642,14 +643,14 @@ int p3I2pBob::stateMachineController_locked_error() { // wait for bob protocoll if (mBOBState != bsCleared) { - rslog(RsLog::Debug_All, &i2pBobLogInfo, "stateMachineController_locked_error: waiting for BOB"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: waiting for BOB" << std::endl; return sleepFactorFast; } #if 0 std::stringstream ss; ss << "stateMachineController_locked_error: mProcessing: " << (mProcessing ? "not null" : "null"); - rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str()); + Dbg4() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; #endif // try to finish ticket @@ -657,7 +658,7 @@ int p3I2pBob::stateMachineController_locked_error() switch (mTask) { case ctRunCheck: // connection check failed at some point - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to check proxy status (it's likely dead)!"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: failed to check proxy status (it's likely dead)!" << std::endl; *((bool *)mProcessing->data) = true; mState = csDoDisconnect; mStateOld = csIdel; @@ -665,7 +666,7 @@ int p3I2pBob::stateMachineController_locked_error() break; case ctRunShutDown: // not a big deal though - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to shut down tunnel (it's likely dead though)!"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: failed to shut down tunnel (it's likely dead though)!" << std::endl; mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); @@ -673,14 +674,14 @@ int p3I2pBob::stateMachineController_locked_error() case ctIdle: // should not happen but we need to deal with it // this will produce some error messages in the log and finish the task (marked as failed) - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: task is idle. This should not happen!"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: task is idle. This should not happen!" << std::endl; mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); break; case ctRunGetKeys: case ctRunSetUp: - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: failed to receive key / start up"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: failed to receive key / start up" << std::endl; mStateOld = csError; mState = csDoDisconnect; // keep the error message @@ -691,7 +692,7 @@ int p3I2pBob::stateMachineController_locked_error() // periodically retry if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) { - rslog(RsLog::Warning, &i2pBobLogInfo, "stateMachineController_locked_error: retrying"); + RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: retrying" << std::endl; mLastProxyCheck = time(NULL); mErrorMsg.clear(); @@ -704,7 +705,7 @@ int p3I2pBob::stateMachineController_locked_error() // check for new tickets if (!mPending.empty()) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "stateMachineController_locked_error: processing new ticket"); + Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: processing new ticket" << std::endl; // reset and try new task mTask = ctIdle; @@ -735,7 +736,7 @@ RsSerialiser *p3I2pBob::setupSerialiser() bool p3I2pBob::saveList(bool &cleanup, std::list &lst) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "saveList"); + Dbg4() << __PRETTY_FUNCTION__ << " saveList" << std::endl; cleanup = true; RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; @@ -770,7 +771,7 @@ bool p3I2pBob::saveList(bool &cleanup, std::list &lst) bool p3I2pBob::loadList(std::list &load) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "loadList"); + Dbg4() << __PRETTY_FUNCTION__ << " loadList" << std::endl; for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { RsConfigKeyValueSet *vitem = dynamic_cast(*it); @@ -790,7 +791,7 @@ bool p3I2pBob::loadList(std::list &load) getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) else - rslog(RsLog::Warning, &i2pBobLogInfo, "loadList unknown key: " + kit->key); + RsDbg() << __PRETTY_FUNCTION__ << " loadList unknown key: " + kit->key << std::endl; } } delete vitem; @@ -854,7 +855,7 @@ void p3I2pBob::getStates(bobStates *bs) std::string p3I2pBob::executeCommand(const std::string &command) { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "executeCommand_locked running '" + command + "'"); + Dbg4() << __PRETTY_FUNCTION__ << " executeCommand_locked running '" + command + "'" << std::endl; std::string copy = command; copy.push_back('\n'); @@ -866,7 +867,7 @@ std::string p3I2pBob::executeCommand(const std::string &command) // receive answer (trailing new line is already removed!) std::string ans = recv(); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "executeCommand_locked answer '" + ans + "'"); + Dbg4() << __PRETTY_FUNCTION__ << " executeCommand_locked answer '" + ans + "'" << std::endl; return ans; } @@ -876,7 +877,7 @@ bool p3I2pBob::connectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket != 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked mSocket != 0"); + RsDbg() << __PRETTY_FUNCTION__ << " connectI2P_locked mSocket != 0" << std::endl; return false; } @@ -884,21 +885,21 @@ bool p3I2pBob::connectI2P() mSocket = unix_socket(PF_INET, SOCK_STREAM, 0); if (mSocket < 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked Failed to open socket! Socket Error: " + socket_errorType(errno)); + RsDbg() << __PRETTY_FUNCTION__ << " connectI2P_locked Failed to open socket! Socket Error: " + socket_errorType(errno) << std::endl; return false; } // connect int err = unix_connect(mSocket, mI2PProxyAddr); if (err != 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "connectI2P_locked Failed to connect to BOB! Socket Error: " + socket_errorType(errno)); + RsDbg() << __PRETTY_FUNCTION__ << " connectI2P_locked Failed to connect to BOB! Socket Error: " + socket_errorType(errno) << std::endl; return false; } // receive hello msg recv(); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "connectI2P_locked done"); + Dbg4() << __PRETTY_FUNCTION__ << " connectI2P_locked done" << std::endl; return true; } @@ -907,17 +908,17 @@ bool p3I2pBob::disconnectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket == 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "disconnectI2P_locked mSocket == 0"); + RsDbg() << __PRETTY_FUNCTION__ << " disconnectI2P_locked mSocket == 0" << std::endl; return true; } int err = unix_close(mSocket); if (err != 0) { - rslog(RsLog::Warning, &i2pBobLogInfo, "disconnectI2P_locked Failed to close socket! Socket Error: " + socket_errorType(errno)); + RsDbg() << __PRETTY_FUNCTION__ << " disconnectI2P_locked Failed to close socket! Socket Error: " + socket_errorType(errno) << std::endl; return false; } - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "disconnectI2P_locked done"); + Dbg4() << __PRETTY_FUNCTION__ << " disconnectI2P_locked done" << std::endl; mSocket = 0; return true; } @@ -938,7 +939,7 @@ std::string toString(const std::string &a, const int8_t b) { void p3I2pBob::finalizeSettings_locked() { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked"); + Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked" << std::endl; sockaddr_storage_clear(mI2PProxyAddr); // get i2p proxy addr @@ -949,8 +950,8 @@ void p3I2pBob::finalizeSettings_locked() sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); sockaddr_storage_setport(mI2PProxyAddr, 2827); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + sockaddr_storage_tostring(mI2PProxyAddr)); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using " + mSetting.address.base32); + Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked using " + sockaddr_storage_tostring(mI2PProxyAddr) << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked using " + mSetting.address.base32 << std::endl; peerState ps; mPeerMgr->getOwnNetStatus(ps); @@ -965,7 +966,7 @@ void p3I2pBob::finalizeSettings_locked() std::vector tmp(len); RSRandom::random_bytes(tmp.data(), len); const std::string location = Radix32::encode(tmp.data(), len); - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "finalizeSettings_locked using suffix " + location); + Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked using suffix " + location << std::endl; mTunnelName = "RetroShare-" + location; const std::string setnick = "setnick RetroShare-" + location; @@ -1033,7 +1034,7 @@ void p3I2pBob::finalizeSettings_locked() void p3I2pBob::updateSettings_locked() { - rslog(RsLog::Debug_Basic, &i2pBobLogInfo, "updateSettings_locked"); + Dbg4() << __PRETTY_FUNCTION__ << " updateSettings_locked" << std::endl; sockaddr_storage proxy; mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); @@ -1095,7 +1096,7 @@ std::string p3I2pBob::recv() #if 0 std::stringstream ss; ss << "recv length: " << length << " (bufferSize: " << bufferSize << ") ans: " << ans.length(); - rslog(RsLog::Debug_All, &i2pBobLogInfo, ss.str()); + Dbg4() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; #endif // clear and resize buffer again From a41e10b178e3903173e535893a0bbaf6f5dbdd26 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 8 May 2020 16:50:59 +0200 Subject: [PATCH 32/89] i2p: bob: remove unused variables --- libretroshare/src/services/autoproxy/p3i2pbob.cc | 5 ----- 1 file changed, 5 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index a68d85396..f63a10b7f 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -44,11 +44,6 @@ static const std::string kConfigKeyOutLength = "OUT_LENGTH"; static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY"; static const std::string kConfigKeyOutVariance = "OUT_VARIANCE"; -static const bool kDefaultBOBEnable = false; -static const int8_t kDefaultLength = 3; -static const int8_t kDefaultQuantity = 4; -static const int8_t kDefaultVariance = 0; - /// Sleep duration for receiving loop static const useconds_t sleepTimeRecv = 10; // times 1000 = 10ms /// Sleep duration for everything else From 1da3d262d94ef68d0289fdfcae243570d842fd85 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 8 May 2020 16:54:51 +0200 Subject: [PATCH 33/89] i2p: bob: removed function name within debug output (replaced by __PRETTY_FUNCTION__) --- .../src/services/autoproxy/p3i2pbob.cc | 102 +++++++++--------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index f63a10b7f..e8ebb5979 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -160,7 +160,7 @@ void p3I2pBob::processTaskAsync(taskTicket *ticket) } break; default: - RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::processTaskAsync unknown task" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " unknown task" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -175,7 +175,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::status: // check if everything needed is set if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::status autoProxyTask::status data is missing" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::status data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -189,7 +189,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::getSettings: // check if everything needed is set if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::data_tick autoProxyTask::getSettings data is missing" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::getSettings data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -203,7 +203,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::setSettings: // check if everything needed is set if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::data_tick autoProxyTask::setSettings data is missing" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::setSettings data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -223,7 +223,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) break; case autoProxyTask::getErrorInfo: if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::data_tick autoProxyTask::getErrorInfo data is missing" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::getErrorInfo data is missing" << std::endl; rsAutoProxyMonitor::taskError(ticket); } else { RS_STACK_MUTEX(mLock); @@ -232,7 +232,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) } break; default: - RsDbg() << __PRETTY_FUNCTION__ << " p3I2pBob::processTaskSync unknown task" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " unknown task" << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -300,7 +300,7 @@ int p3I2pBob::stateMachineBOB() } if (answer.find(mTunnelName) == std::string::npos) { - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineBOB status check: tunnel down!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " status check: tunnel down!" << std::endl; // signal error *((bool *)mProcessing->data) = true; } @@ -340,8 +340,8 @@ int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bo return sleepFactorDefault; } - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineBOB FAILED to run command '" + currentState.command + "'" << std::endl; - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineBOB '" + answer + "'" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " FAILED to run command '" + currentState.command + "'" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " '" + answer + "'" << std::endl; mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n'); mErrorMsg.append("reason '" + answer + "'" + '\n'); @@ -388,14 +388,14 @@ int p3I2pBob::stateMachineController() return stateMachineController_locked_idle(); case csDoConnect: if (!connectI2P()) { - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController doConnect: unable to connect" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " doConnect: unable to connect" << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "unable to connect to BOB port"; return sleepFactorSlow; } - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController doConnect: connected" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " doConnect: connected" << std::endl; mState = csConnected; break; case csConnected: @@ -403,7 +403,7 @@ int p3I2pBob::stateMachineController() case csWaitForBob: // check connection problems if (mSocket == 0) { - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController waitForBob: conection lost" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " waitForBob: conection lost" << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "connection lost to BOB"; @@ -413,21 +413,21 @@ int p3I2pBob::stateMachineController() // check for finished BOB protocol if (mBOBState == bsCleared) { // done - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController waitForBob: mBOBState == bsCleared" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " waitForBob: mBOBState == bsCleared" << std::endl; mState = csDoDisconnect; } break; case csDoDisconnect: if (!disconnectI2P() || mSocket != 0) { // just in case - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController doDisconnect: can't disconnect" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " doDisconnect: can't disconnect" << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "unable to disconnect from BOB"; return sleepFactorDefault; } - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController doDisconnect: disconnected" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " doDisconnect: disconnected" << std::endl; mState = csDisconnected; break; case csDisconnected: @@ -458,7 +458,7 @@ int p3I2pBob::stateMachineController_locked_idle() mProcessing->task == autoProxyTask::stop || mProcessing->task == autoProxyTask::proxyStatusCheck)) { // skip since we are not enabled - Dbg1() << __PRETTY_FUNCTION__ << " stateMachineController_locked_idle: disabled -> skipping ticket" << std::endl; + Dbg1() << __PRETTY_FUNCTION__ << " disabled -> skipping ticket" << std::endl; rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled); mProcessing = NULL; } else { @@ -480,7 +480,7 @@ int p3I2pBob::stateMachineController_locked_idle() mTask = ctRunCheck; break; default: - Dbg1() << __PRETTY_FUNCTION__ << " stateMachineController_locked_idle unknown async task" << std::endl; + Dbg1() << __PRETTY_FUNCTION__ << " unknown async task" << std::endl; rsAutoProxyMonitor::taskError(mProcessing); mProcessing = NULL; break; @@ -528,28 +528,28 @@ int p3I2pBob::stateMachineController_locked_connected() case ctRunSetUp: // when we have a key use it for server tunnel! if(mSetting.address.privateKey.empty()) { - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = setnickC" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = setnickC" << std::endl; mBOBState = bsSetnickC; } else { - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = setnickS" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = setnickS" << std::endl; mBOBState = bsSetnickS; } break; case ctRunShutDown: // shut down existing tunnel - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = getnick" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = getnick" << std::endl; mBOBState = bsGetnick; break; case ctRunCheck: - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = list" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = list" << std::endl; mBOBState = bsList; break; case ctRunGetKeys: - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: setting mBOBState = setnickN" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = setnickN" << std::endl; mBOBState = bsSetnickN; break; case ctIdle: - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_connected: task is idle. This should not happen!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " task is idle. This should not happen!" << std::endl; break; } @@ -565,7 +565,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() if(errorHappened) { // reset old state mStateOld = csIdel; - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: error during process!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " error during process!" << std::endl; } // answer ticket @@ -594,12 +594,12 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; if (!errorHappened) { - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: run check result: ok" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " run check result: ok" << std::endl; break; } // switch to error newState = csError; - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: run check result: error" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " run check result: error" << std::endl; mErrorMsg = "Connection check failed. Will try to restart tunnel."; break; @@ -622,7 +622,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; break; case ctIdle: - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_disconnected: task is idle. This should not happen!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " task is idle. This should not happen!" << std::endl; rsAutoProxyMonitor::taskError(mProcessing); } mProcessing = NULL; @@ -638,7 +638,7 @@ int p3I2pBob::stateMachineController_locked_error() { // wait for bob protocoll if (mBOBState != bsCleared) { - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: waiting for BOB" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " waiting for BOB" << std::endl; return sleepFactorFast; } @@ -653,7 +653,7 @@ int p3I2pBob::stateMachineController_locked_error() switch (mTask) { case ctRunCheck: // connection check failed at some point - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: failed to check proxy status (it's likely dead)!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " failed to check proxy status (it's likely dead)!" << std::endl; *((bool *)mProcessing->data) = true; mState = csDoDisconnect; mStateOld = csIdel; @@ -661,7 +661,7 @@ int p3I2pBob::stateMachineController_locked_error() break; case ctRunShutDown: // not a big deal though - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: failed to shut down tunnel (it's likely dead though)!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " failed to shut down tunnel (it's likely dead though)!" << std::endl; mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); @@ -669,14 +669,14 @@ int p3I2pBob::stateMachineController_locked_error() case ctIdle: // should not happen but we need to deal with it // this will produce some error messages in the log and finish the task (marked as failed) - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: task is idle. This should not happen!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " task is idle. This should not happen!" << std::endl; mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); break; case ctRunGetKeys: case ctRunSetUp: - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: failed to receive key / start up" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " failed to receive key / start up" << std::endl; mStateOld = csError; mState = csDoDisconnect; // keep the error message @@ -687,7 +687,7 @@ int p3I2pBob::stateMachineController_locked_error() // periodically retry if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) { - RsDbg() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: retrying" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " retrying" << std::endl; mLastProxyCheck = time(NULL); mErrorMsg.clear(); @@ -700,7 +700,7 @@ int p3I2pBob::stateMachineController_locked_error() // check for new tickets if (!mPending.empty()) { - Dbg4() << __PRETTY_FUNCTION__ << " stateMachineController_locked_error: processing new ticket" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " processing new ticket" << std::endl; // reset and try new task mTask = ctIdle; @@ -731,7 +731,7 @@ RsSerialiser *p3I2pBob::setupSerialiser() bool p3I2pBob::saveList(bool &cleanup, std::list &lst) { - Dbg4() << __PRETTY_FUNCTION__ << " saveList" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << std::endl; cleanup = true; RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; @@ -766,7 +766,7 @@ bool p3I2pBob::saveList(bool &cleanup, std::list &lst) bool p3I2pBob::loadList(std::list &load) { - Dbg4() << __PRETTY_FUNCTION__ << " loadList" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << std::endl; for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { RsConfigKeyValueSet *vitem = dynamic_cast(*it); @@ -786,7 +786,7 @@ bool p3I2pBob::loadList(std::list &load) getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) else - RsDbg() << __PRETTY_FUNCTION__ << " loadList unknown key: " + kit->key << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " unknown key: " + kit->key << std::endl; } } delete vitem; @@ -850,7 +850,7 @@ void p3I2pBob::getStates(bobStates *bs) std::string p3I2pBob::executeCommand(const std::string &command) { - Dbg4() << __PRETTY_FUNCTION__ << " executeCommand_locked running '" + command + "'" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " running '" + command + "'" << std::endl; std::string copy = command; copy.push_back('\n'); @@ -862,7 +862,7 @@ std::string p3I2pBob::executeCommand(const std::string &command) // receive answer (trailing new line is already removed!) std::string ans = recv(); - Dbg4() << __PRETTY_FUNCTION__ << " executeCommand_locked answer '" + ans + "'" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " answer '" + ans + "'" << std::endl; return ans; } @@ -872,7 +872,7 @@ bool p3I2pBob::connectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket != 0) { - RsDbg() << __PRETTY_FUNCTION__ << " connectI2P_locked mSocket != 0" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " mSocket != 0" << std::endl; return false; } @@ -880,21 +880,21 @@ bool p3I2pBob::connectI2P() mSocket = unix_socket(PF_INET, SOCK_STREAM, 0); if (mSocket < 0) { - RsDbg() << __PRETTY_FUNCTION__ << " connectI2P_locked Failed to open socket! Socket Error: " + socket_errorType(errno) << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " Failed to open socket! Socket Error: " + socket_errorType(errno) << std::endl; return false; } // connect int err = unix_connect(mSocket, mI2PProxyAddr); if (err != 0) { - RsDbg() << __PRETTY_FUNCTION__ << " connectI2P_locked Failed to connect to BOB! Socket Error: " + socket_errorType(errno) << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " Failed to connect to BOB! Socket Error: " + socket_errorType(errno) << std::endl; return false; } // receive hello msg recv(); - Dbg4() << __PRETTY_FUNCTION__ << " connectI2P_locked done" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " done" << std::endl; return true; } @@ -903,17 +903,17 @@ bool p3I2pBob::disconnectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket == 0) { - RsDbg() << __PRETTY_FUNCTION__ << " disconnectI2P_locked mSocket == 0" << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " mSocket == 0" << std::endl; return true; } int err = unix_close(mSocket); if (err != 0) { - RsDbg() << __PRETTY_FUNCTION__ << " disconnectI2P_locked Failed to close socket! Socket Error: " + socket_errorType(errno) << std::endl; + RsDbg() << __PRETTY_FUNCTION__ << " Failed to close socket! Socket Error: " + socket_errorType(errno) << std::endl; return false; } - Dbg4() << __PRETTY_FUNCTION__ << " disconnectI2P_locked done" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " done" << std::endl; mSocket = 0; return true; } @@ -934,7 +934,7 @@ std::string toString(const std::string &a, const int8_t b) { void p3I2pBob::finalizeSettings_locked() { - Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << std::endl; sockaddr_storage_clear(mI2PProxyAddr); // get i2p proxy addr @@ -945,8 +945,8 @@ void p3I2pBob::finalizeSettings_locked() sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); sockaddr_storage_setport(mI2PProxyAddr, 2827); - Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked using " + sockaddr_storage_tostring(mI2PProxyAddr) << std::endl; - Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked using " + mSetting.address.base32 << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " using " + sockaddr_storage_tostring(mI2PProxyAddr) << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " using " + mSetting.address.base32 << std::endl; peerState ps; mPeerMgr->getOwnNetStatus(ps); @@ -961,7 +961,7 @@ void p3I2pBob::finalizeSettings_locked() std::vector tmp(len); RSRandom::random_bytes(tmp.data(), len); const std::string location = Radix32::encode(tmp.data(), len); - Dbg4() << __PRETTY_FUNCTION__ << " finalizeSettings_locked using suffix " + location << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << " using suffix " + location << std::endl; mTunnelName = "RetroShare-" + location; const std::string setnick = "setnick RetroShare-" + location; @@ -1029,7 +1029,7 @@ void p3I2pBob::finalizeSettings_locked() void p3I2pBob::updateSettings_locked() { - Dbg4() << __PRETTY_FUNCTION__ << " updateSettings_locked" << std::endl; + Dbg4() << __PRETTY_FUNCTION__ << std::endl; sockaddr_storage proxy; mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); From f4f08f037922ac292d509c62812789cc06803bb1 Mon Sep 17 00:00:00 2001 From: sehraf Date: Sun, 10 May 2020 13:37:53 +0200 Subject: [PATCH 34/89] i2p: bob: rework recv() to check for new line character as EOL indicator --- .../src/services/autoproxy/p3i2pbob.cc | 72 ++++++++++++------- 1 file changed, 48 insertions(+), 24 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index e8ebb5979..ef8e1ad48 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -44,8 +44,8 @@ static const std::string kConfigKeyOutLength = "OUT_LENGTH"; static const std::string kConfigKeyOutQuantity = "OUT_QUANTITY"; static const std::string kConfigKeyOutVariance = "OUT_VARIANCE"; -/// Sleep duration for receiving loop -static const useconds_t sleepTimeRecv = 10; // times 1000 = 10ms +/// Sleep duration for receiving loop in error/no-data case +static const useconds_t sleepTimeRecv = 250; // times 1000 = 250ms /// Sleep duration for everything else static const useconds_t sleepTimeWait = 50; // times 1000 = 50ms or 0.05s static const int sleepFactorDefault = 10; // 0.5s @@ -1069,38 +1069,62 @@ void p3I2pBob::updateSettings_locked() std::string p3I2pBob::recv() { + // BOB works line based + // -> \n indicates and of the line + + constexpr uint16_t bufferSize = 128; + char buffer[bufferSize]; + std::string ans; - ssize_t length; - const uint16_t bufferSize = 128; - std::vector buffer(bufferSize); + uint16_t retry = 10; do { - doSleep(sleepTimeRecv); + memset(buffer, 0, bufferSize); - // there is only one thread that touches mSocket - no need for a lock - length = ::recv(mSocket, buffer.data(), buffer.size(), 0); - if (length < 0) + // peek at data + auto length = ::recv(mSocket, buffer, bufferSize, MSG_PEEK); + if (length <= 0) { + if (length < 0) { + // error + perror(__PRETTY_FUNCTION__); + } + retry--; + doSleep(sleepTimeRecv); continue; + } - ans.append(buffer.begin(), buffer.end()); + // at least one byte was read - // clean received string - ans.erase(std::remove(ans.begin(), ans.end(), '\0'), ans.end()); - ans.erase(std::remove(ans.begin(), ans.end(), '\n'), ans.end()); + // search for new line + auto bufferStr = std::string(buffer); + size_t pos = bufferStr.find('\n'); -#if 0 - std::stringstream ss; - ss << "recv length: " << length << " (bufferSize: " << bufferSize << ") ans: " << ans.length(); - Dbg4() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; -#endif + if (pos == std::string::npos) { + // no new line found -> more to read - // clear and resize buffer again - buffer.clear(); - buffer.resize(bufferSize); + // sanity check + if (length != bufferSize) { + // expectation: a full buffer was peeked) + Dbg1() << __PRETTY_FUNCTION__ << " peeked less than bufferSize but also didn't found a new line character" << std::endl; + } + // this should never happen + assert(length <= bufferSize); + } else { + // new line found -> end of message - if (this->shouldStop()) - break; - } while(length == bufferSize || ans.size() < 4); + // calculate how much there is to read, read the \n, too! + length = pos + 1; + + // end loop + retry = 0; + } + + // now read for real + memset(buffer, 0, bufferSize); + length = ::recv(mSocket, buffer, length, 0); + bufferStr = std::string(buffer); + ans.append(bufferStr); + } while(retry > 0); return ans; } From b6a550b8f5509ce9e97da65614e2cfbd87cab32b Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 21 May 2020 10:12:59 +0200 Subject: [PATCH 35/89] autoproxy: make use of rsdebug.h --- .../services/autoproxy/rsautoproxymonitor.cc | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index d198bd207..263663a4e 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -22,6 +22,7 @@ #include "rsautoproxymonitor.h" #include /* for usleep() */ +#include "util/rsdebug.h" #include "util/rstime.h" rsAutoProxyMonitor *rsAutoProxyMonitor::mInstance = NULL; @@ -42,8 +43,10 @@ rsAutoProxyMonitor *rsAutoProxyMonitor::instance() void rsAutoProxyMonitor::addProxy(autoProxyType::autoProxyType_enum type, autoProxyService *service) { RS_STACK_MUTEX(mLock); - if (mProxies.find(type) != mProxies.end()) - std::cerr << "sAutoProxyMonitor::addProxy type " << type << " already added - OVERWRITING" << std::endl; + if (mProxies.find(type) != mProxies.end()) { + RsErr() << __PRETTY_FUNCTION__<< " type " << type << " already added - OVERWRITING" << std::endl; + print_stacktrace(); + } mProxies[type] = service; } @@ -117,7 +120,7 @@ void rsAutoProxyMonitor::stopAllRSShutdown() do { rstime::rs_usleep(1000 * 1000); RS_STACK_MUTEX(mLock); - std::cout << "(II) waiting for auto proxy service(s) to shut down " << t << "/" << timeout << " (remaining: " << mProxies.size() << ")" << std::endl; + RsDbg() << __PRETTY_FUNCTION__<< " waiting for auto proxy service(s) to shut down " << t << "/" << timeout << " (remaining: " << mProxies.size() << ")" << std::endl; if (mProxies.empty()) break; t++; @@ -146,13 +149,16 @@ void rsAutoProxyMonitor::task(taskTicket *ticket) { // sanity checks if (!ticket->async && ticket->types.size() > 1) { - std::cerr << "(WW) rsAutoProxyMonitor::task synchronous call to multiple services. This can cause problems!" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " synchronous call to multiple services. This can cause problems!" << std::endl; + print_stacktrace(); } if (ticket->async && !ticket->cb && ticket->data) { - std::cerr << "(WW) rsAutoProxyMonitor::task asynchronous call with data but no callback. This will likely causes memory leak!" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " asynchronous call with data but no callback. This will likely causes memory leak!" << std::endl; + print_stacktrace(); } if (ticket->types.size() > 1 && ticket->data) { - std::cerr << "(WW) rsAutoProxyMonitor::task call with data to multiple services. This will likely causes memory leak!" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " call with data to multiple services. This will likely causes memory leak!" << std::endl; + print_stacktrace(); } std::vector::const_iterator it; @@ -187,7 +193,8 @@ void rsAutoProxyMonitor::taskAsync(std::vector if (isAsyncTask(task)) { // Usually the services will reject this ticket. // Just print a warning - maybe there is some special case where this is a good idea. - std::cerr << "(WW) rsAutoProxyMonitor::taskSync called with an asynchronous task!" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " called with an asynchronous task!" << std::endl; + print_stacktrace(); } taskTicket *tt = getTicket(); @@ -244,7 +252,8 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu t->cb->taskFinished(t); if (t != NULL) { // callack did not clean up properly - std::cerr << "(WW) rsAutoProxyMonitor::taskFinish callback did not clean up!" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " callback did not clean up!" << std::endl; + print_stacktrace(); cleanUp = true; } } else if (t->async){ @@ -252,12 +261,13 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu // we must take care of deleting cleanUp = true; if(t->data) - std::cerr << "(WW) rsAutoProxyMonitor::taskFinish async call with data attached but no callback set!" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " async call with data attached but no callback set!" << std::endl; } if (cleanUp) { if (t->data) { - std::cerr << "(WW) rsAutoProxyMonitor::taskFinish will try to delete void pointer!" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " will try to delete void pointer!" << std::endl; + print_stacktrace(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-incomplete" delete t->data; @@ -290,7 +300,8 @@ void rsAutoProxyMonitor::taskFinished(taskTicket *&ticket) // clean up if (ticket->data) { - std::cerr << "rsAutoProxyMonitor::taskFinished data set. Will try to delete void pointer" << std::endl; + RsErr() << __PRETTY_FUNCTION__<< " data set. Will try to delete void pointer" << std::endl; + print_stacktrace(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-incomplete" delete ticket->data; @@ -308,7 +319,7 @@ autoProxyService *rsAutoProxyMonitor::lookUpService(autoProxyType::autoProxyType if ((itService = mProxies.find(t)) != mProxies.end()) { return itService->second; } - std::cerr << "sAutoProxyMonitor::lookUpService no service for type " << t << " found!" << std::endl; + RsDbg() << __PRETTY_FUNCTION__<< " no service for type " << t << " found!" << std::endl; return NULL; } From 9a5504bb4733384763a648222f39c636c73dd541 Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 21 May 2020 10:17:13 +0200 Subject: [PATCH 36/89] autoproxy: make async really async --- libretroshare/src/services/autoproxy/rsautoproxymonitor.cc | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index 263663a4e..62b47dfea 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -174,7 +174,11 @@ void rsAutoProxyMonitor::task(taskTicket *ticket) *tt = *ticket; tt->types.clear(); tt->types.push_back(*it); - s->processTaskAsync(tt); + + // it's async! + RsThread::async([s, tt] { + s->processTaskAsync(tt); + }); } else { s->processTaskSync(ticket); } From a5a2b49d992df09249cff439a7474351643ae7f3 Mon Sep 17 00:00:00 2001 From: sehraf Date: Mon, 25 May 2020 21:51:06 +0200 Subject: [PATCH 37/89] i2p: bob: add option to disable bob CONFIG+=no_rs_bob --- libretroshare/src/rsserver/p3face.h | 2 ++ libretroshare/src/rsserver/rsinit.cc | 6 ++++++ retroshare-gui/src/gui/settings/ServerPage.cpp | 4 ++++ retroshare.pri | 9 +++++++++ 4 files changed, 21 insertions(+) diff --git a/libretroshare/src/rsserver/p3face.h b/libretroshare/src/rsserver/p3face.h index a2637ceaf..661cb244f 100644 --- a/libretroshare/src/rsserver/p3face.h +++ b/libretroshare/src/rsserver/p3face.h @@ -161,7 +161,9 @@ public: p3ChatService *chatSrv; p3StatusService *mStatusSrv; p3GxsTunnelService *mGxsTunnels; +#ifdef RS_USE_I2P_BOB p3I2pBob *mI2pBob; +#endif // This list contains all threaded services. It will be used to shut them down properly. diff --git a/libretroshare/src/rsserver/rsinit.cc b/libretroshare/src/rsserver/rsinit.cc index 1c4e2a062..fa21b82e7 100644 --- a/libretroshare/src/rsserver/rsinit.cc +++ b/libretroshare/src/rsserver/rsinit.cc @@ -923,8 +923,10 @@ int RsServer::StartupRetroShare() mNetMgr->setManagers(mPeerMgr, mLinkMgr); rsAutoProxyMonitor *autoProxy = rsAutoProxyMonitor::instance(); +#ifdef RS_USE_I2P_BOB mI2pBob = new p3I2pBob(mPeerMgr); autoProxy->addProxy(autoProxyType::I2PBOB, mI2pBob); +#endif //load all the SSL certs as friends // std::list sslIds; @@ -1649,7 +1651,9 @@ int RsServer::StartupRetroShare() mConfigMgr->addConfiguration("wire.cfg", wire_ns); #endif #endif //RS_ENABLE_GXS +#ifdef RS_USE_I2P_BOB mConfigMgr->addConfiguration("I2PBOB.cfg", mI2pBob); +#endif mPluginsManager->addConfigurations(mConfigMgr) ; @@ -1795,7 +1799,9 @@ int RsServer::StartupRetroShare() /**************************************************************************/ // auto proxy threads +#ifdef RS_USE_I2P_BOB startServiceThread(mI2pBob, "I2P-BOB"); +#endif #ifdef RS_ENABLE_GXS // Must Set the GXS pointers before starting threads. diff --git a/retroshare-gui/src/gui/settings/ServerPage.cpp b/retroshare-gui/src/gui/settings/ServerPage.cpp index f0f8566e0..37291be0f 100755 --- a/retroshare-gui/src/gui/settings/ServerPage.cpp +++ b/retroshare-gui/src/gui/settings/ServerPage.cpp @@ -83,6 +83,10 @@ ServerPage::ServerPage(QWidget * parent, Qt::WindowFlags flags) manager = NULL ; mOngoingConnectivityCheck = -1; +#ifndef RS_USE_I2P_BOB + ui.hiddenServiceTab->removeTab(TAB_HIDDEN_SERVICE_I2P_BOB); // warning: the order of operation here is very important. +#endif + if(RsAccounts::isHiddenNode()) { if(RsAccounts::isTorAuto()) diff --git a/retroshare.pri b/retroshare.pri index 6bccc1f5a..994ced0f2 100644 --- a/retroshare.pri +++ b/retroshare.pri @@ -140,6 +140,11 @@ rs_macos10.15:CONFIG -= rs_macos10.11 CONFIG *= no_rs_jsonapi rs_jsonapi:CONFIG -= no_rs_jsonapi +# Disable i2p BOB support for automatically setting up an i2p tunnel for RS +# "CONFIG+=no_rs_bob" +CONFIG *= rs_bob +no_rs_bob:CONFIG -= rs_bob + # To enable channel indexing append the following assignation to qmake command # line "CONFIG+=rs_deep_channel_index" CONFIG *= no_rs_deep_channel_index @@ -550,6 +555,10 @@ rs_webui { DEFINES *= RS_WEBUI } +rs_bob { + DEFINES *= RS_USE_I2P_BOB +} + rs_deep_channels_index:DEFINES *= RS_DEEP_CHANNEL_INDEX rs_deep_files_index:DEFINES *= RS_DEEP_FILES_INDEX From 950703c398f7f5bc920b04c60433f612c677020e Mon Sep 17 00:00:00 2001 From: sehraf Date: Mon, 25 May 2020 22:23:58 +0200 Subject: [PATCH 38/89] i2p: bob: handle CONFIG+=no_rs_bob when creating a new location --- retroshare-gui/src/gui/GenCertDialog.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/retroshare-gui/src/gui/GenCertDialog.cpp b/retroshare-gui/src/gui/GenCertDialog.cpp index 4fdb9f714..aae80b753 100644 --- a/retroshare-gui/src/gui/GenCertDialog.cpp +++ b/retroshare-gui/src/gui/GenCertDialog.cpp @@ -340,6 +340,10 @@ void GenCertDialog::setupState() ui.hiddenport_spinBox->setVisible(hidden_state && !tor_auto); ui.cbUseBob->setVisible(hidden_state && !tor_auto); +#ifndef RS_USE_I2P_BOB + ui.cbUseBob->setDisabled(true); + ui.cbUseBob->setToolTip(tr("BOB support is not available")); +#endif if(!mAllFieldsOk) { From 85ce2c0f33aba78fc9b1c3ba51a657eb4492b691 Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 10 Jun 2020 20:08:23 +0200 Subject: [PATCH 39/89] fixed bug in files model --- retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp | 2 +- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 8942b65b0..7a5ae4a34 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -84,7 +84,7 @@ int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const return 0; if(!parent.isValid()) - return (mFiles.size() + COLUMN_FILES_NB_COLUMNS-1)/COLUMN_FILES_NB_COLUMNS; // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 + return mFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; return 0; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 375290f61..b5d8c289d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -139,6 +139,8 @@ private slots: void settingsChanged(); void handlePostsTreeSizeChange(QSize s); void updateChannelFiles(); + +public slots: void sortColumn(int col,Qt::SortOrder so); private: From 8c3c973d02c94c993662c5802e5f7c8e01bb80d1 Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 10 Jun 2020 22:04:34 +0200 Subject: [PATCH 40/89] changed filtering model to a hand-made solution --- .../gxschannels/GxsChannelPostFilesModel.cpp | 74 +++++++++--- .../gxschannels/GxsChannelPostFilesModel.h | 7 +- .../GxsChannelPostsWidgetWithModel.cpp | 107 +++++++++--------- .../GxsChannelPostsWidgetWithModel.h | 5 +- 4 files changed, 117 insertions(+), 76 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 7a5ae4a34..86dc330cc 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -42,17 +42,18 @@ static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// define RsGxsChannelPostFilesModel::RsGxsChannelPostFilesModel(QObject *parent) : QAbstractItemModel(parent) { - initEmptyHierarchy(mFiles); + initEmptyHierarchy(); mTimer = new QTimer; connect(mTimer,SIGNAL(timeout()),this,SLOT(update())); } -void RsGxsChannelPostFilesModel::initEmptyHierarchy(std::vector& files) +void RsGxsChannelPostFilesModel::initEmptyHierarchy() { preMods(); mFiles.clear(); + mFilteredFiles.clear(); postMods(); } @@ -67,12 +68,12 @@ void RsGxsChannelPostFilesModel::postMods() { endResetModel(); - emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); } void RsGxsChannelPostFilesModel::update() { - emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); + emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredFiles.size(),COLUMN_FILES_NB_COLUMNS-1,(void*)NULL)); } int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const @@ -80,11 +81,11 @@ int RsGxsChannelPostFilesModel::rowCount(const QModelIndex& parent) const if(parent.column() > 0) return 0; - if(mFiles.empty()) // security. Should never happen. + if(mFilteredFiles.empty()) // security. Should never happen. return 0; if(!parent.isValid()) - return mFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 + return mFilteredFiles.size(); // mFilteredPosts always has an item at 0, so size()>=1, and mColumn>=1 RsErr() << __PRETTY_FUNCTION__ << " rowCount cannot figure out the porper number of rows." << std::endl; return 0; @@ -116,7 +117,7 @@ bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,RsGxsFile& fmp if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) return false ; - fmpe = mFiles[entry]; + fmpe = mFiles[mFilteredFiles[entry]]; return true; @@ -214,7 +215,7 @@ quintptr RsGxsChannelPostFilesModel::getParentRow(quintptr ref,int& row) const { ChannelPostFilesModelIndex ref_entry; - if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFiles.size()) + if(!convertRefPointerToTabEntry(ref,ref_entry) || ref_entry >= mFilteredFiles.size()) return 0 ; if(ref_entry == 0) @@ -284,7 +285,7 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co return QVariant() ; } - if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFiles.size()) + if(!convertRefPointerToTabEntry(ref,entry) || entry >= mFilteredFiles.size()) { #ifdef DEBUG_CHANNEL_MODEL std::cerr << "Bad pointer: " << (void*)ref << std::endl; @@ -292,7 +293,7 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co return QVariant() ; } - const RsGxsFile& fmpe(mFiles[entry]); + const RsGxsFile& fmpe(mFiles[mFilteredFiles[entry]]); #ifdef TODO if(role == Qt::FontRole) @@ -331,6 +332,44 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co } } +void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t& count) +{ + preMods(); + + beginRemoveRows(QModelIndex(),0,rowCount()-1); + endRemoveRows(); + + if(strings.empty()) + { + mFilteredFiles.clear(); + for(int i=0;i& files) { preMods(); - beginRemoveRows(QModelIndex(),0,mFiles.size()-1); + beginRemoveRows(QModelIndex(),0,mFilteredFiles.size()-1); endRemoveRows(); - mFiles.clear(); - initEmptyHierarchy(mFiles); + initEmptyHierarchy(); for(auto& file:files) mFiles.push_back(file); + for(uint32_t i=0;i& files) // debug_dump(); #endif - beginInsertRows(QModelIndex(),0,mFiles.size()-1); + beginInsertRows(QModelIndex(),0,mFilteredFiles.size()-1); endInsertRows(); postMods(); @@ -655,6 +695,7 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list& files) mTimer->stop(); } +#ifdef DEBUG_FORUMMODEL QModelIndex RsGxsChannelPostFilesModel::getIndexOfFile(const RsFileHash& hash) const { // Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map. @@ -671,7 +712,6 @@ QModelIndex RsGxsChannelPostFilesModel::getIndexOfFile(const RsFileHash& hash) c return QModelIndex(); } -#ifdef DEBUG_FORUMMODEL static void recursPrintModel(const std::vector& entries,ForumModelIndex index,int depth) { const ForumModelPostEntry& e(entries[index]); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index e7dd47802..17563fa6d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -64,12 +64,13 @@ public: #endif QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} - QModelIndex getIndexOfFile(const RsFileHash& hash) const; // This method will asynchroneously update the data void setFiles(const std::list& files); + void setFilter(const QStringList &strings, uint32_t &count) ; #ifdef TODO + QModelIndex getIndexOfFile(const RsFileHash& hash) const; void setSortMode(SortMode mode) ; void setTextColorRead (QColor color) { mTextColorRead = color;} @@ -77,7 +78,6 @@ public: void setTextColorUnreadChildren(QColor color) { mTextColorUnreadChildren = color;} void setTextColorNotSubscribed (QColor color) { mTextColorNotSubscribed = color;} void setTextColorMissing (QColor color) { mTextColorMissing = color;} - void setFilter(int column, const QStringList &strings, uint32_t &count) ; void setAuthorOpinion(const QModelIndex& indx,RsOpinion op); #endif @@ -149,8 +149,9 @@ private: #ifdef TODO static void generateMissingItem(const RsGxsMessageId &msgId,ChannelPostsModelPostEntry& entry); #endif - void initEmptyHierarchy(std::vector &files); + void initEmptyHierarchy(); + std::vector mFilteredFiles ; // store the list of files for the post std::vector mFiles ; // store the list of files for the post QTimer *mTimer; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index d6a317151..d7b565e0b 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -240,39 +240,39 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con } } -class RsGxsChannelPostFilesProxyModel: public QSortFilterProxyModel -{ -public: - RsGxsChannelPostFilesProxyModel(QObject *parent = NULL): QSortFilterProxyModel(parent) {} - - bool lessThan(const QModelIndex& left, const QModelIndex& right) const override - { - return left.data(RsGxsChannelPostFilesModel::SortRole) < right.data(RsGxsChannelPostFilesModel::SortRole) ; - } - - bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override - { - if(filter_list.empty()) - return true; - - QString name = sourceModel()->data(sourceModel()->index(source_row,RsGxsChannelPostFilesModel::COLUMN_FILES_NAME,source_parent)).toString(); - - for(auto& s:filter_list) - if(!name.contains(s,Qt::CaseInsensitive)) - return false; - - return true; - } - - void setFilterList(const QStringList& str) - { - filter_list = str; - invalidateFilter(); - } - -private: - QStringList filter_list; -}; +// class RsGxsChannelPostFilesProxyModel: public QSortFilterProxyModel +// { +// public: +// RsGxsChannelPostFilesProxyModel(QObject *parent = NULL): QSortFilterProxyModel(parent) {} +// +// bool lessThan(const QModelIndex& left, const QModelIndex& right) const override +// { +// return left.data(RsGxsChannelPostFilesModel::SortRole) < right.data(RsGxsChannelPostFilesModel::SortRole) ; +// } +// +// bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override +// { +// if(filter_list.empty()) +// return true; +// +// QString name = sourceModel()->data(sourceModel()->index(source_row,RsGxsChannelPostFilesModel::COLUMN_FILES_NAME,source_parent)).toString(); +// +// for(auto& s:filter_list) +// if(!name.contains(s,Qt::CaseInsensitive)) +// return false; +// +// return true; +// } +// +// void setFilterList(const QStringList& str) +// { +// filter_list = str; +// invalidateFilter(); +// } +// +// private: +// QStringList filter_list; +// }; /** Constructor */ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : @@ -285,24 +285,25 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel()); ui->postsTree->setItemDelegate(new ChannelPostDelegate()); - mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this); - - mChannelPostFilesProxyModel = new RsGxsChannelPostFilesProxyModel(this); - mChannelPostFilesProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); - mChannelPostFilesProxyModel->setSourceModel(mChannelPostFilesModel); - mChannelPostFilesProxyModel->setDynamicSortFilter(true); - - ui->channelPostFiles_TV->setModel(mChannelPostFilesProxyModel); + ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this)); ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); - ui->channelPostFiles_TV->setPlaceholderText(tr("Post files")); + ui->channelPostFiles_TV->setPlaceholderText(tr("No files in this post, or no post selected")); ui->channelPostFiles_TV->setSortingEnabled(true); ui->channelPostFiles_TV->sortByColumn(0, Qt::AscendingOrder); - ui->channelFiles_TV->setPlaceholderText(tr("No files in the channel, or no channel selected")); +// mChannelPostFilesProxyModel = new RsGxsChannelPostFilesProxyModel(this); +// mChannelPostFilesProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); +// mChannelPostFilesProxyModel->setSourceModel(mChannelPostFilesModel); +// mChannelPostFilesProxyModel->setDynamicSortFilter(true); + ui->channelFiles_TV->setModel(mChannelFilesModel = new RsGxsChannelPostFilesModel()); ui->channelFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); + ui->channelFiles_TV->setPlaceholderText(tr("No files in the channel, or no channel selected")); + ui->channelFiles_TV->setSortingEnabled(true); + ui->channelFiles_TV->sortByColumn(0, Qt::AscendingOrder); + + //connect(ui->channelPostFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumn(int,Qt::SortOrder))); - connect(ui->channelPostFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumn(int,Qt::SortOrder))); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); connect(mChannelPostsModel,SIGNAL(channelLoaded()),this,SLOT(updateChannelFiles())); @@ -364,11 +365,11 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI }, mEventHandlerId, RsEventType::GXS_CHANNELS ); } -void GxsChannelPostsWidgetWithModel::sortColumn(int col,Qt::SortOrder so) -{ - std::cerr << "Sorting!!"<< std::endl; - mChannelPostFilesProxyModel->sort(col,so); -} +//void GxsChannelPostsWidgetWithModel::sortColumn(int col,Qt::SortOrder so) +//{ +// std::cerr << "Sorting!!"<< std::endl; +// mChannelPostFilesProxyModel->sort(col,so); +//} void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s) { @@ -469,7 +470,7 @@ void GxsChannelPostsWidgetWithModel::updateChannelFiles() ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelFiles_TV->setAutoSelect(true); - mChannelPostFilesProxyModel->sort(0, Qt::AscendingOrder); + //mChannelPostFilesProxyModel->sort(0, Qt::AscendingOrder); } void GxsChannelPostsWidgetWithModel::updateGroupData() @@ -786,10 +787,10 @@ void GxsChannelPostsWidgetWithModel::filterChanged(QString s) QStringList ql = s.split(' ',QString::SkipEmptyParts); uint32_t count; mChannelPostsModel->setFilter(ql,count); + mChannelFilesModel->setFilter(ql,count); - mChannelPostFilesProxyModel->setFilterKeyColumn(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); - mChannelPostFilesProxyModel->setFilterList(ql); - mChannelPostFilesProxyModel->setFilterRegExp(s) ;// triggers a re-display. s is actually not used. + //mChannelPostFilesProxyModel->setFilterKeyColumn(RsGxsChannelPostFilesModel::COLUMN_FILES_NAME); + //mChannelPostFilesProxyModel->setFilterRegExp(s) ;// triggers a re-display. s is actually not used. } #ifdef TODO diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index b5d8c289d..cf7ead667 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -140,8 +140,8 @@ private slots: void handlePostsTreeSizeChange(QSize s); void updateChannelFiles(); -public slots: - void sortColumn(int col,Qt::SortOrder so); +// public slots: +// void sortColumn(int col,Qt::SortOrder so); private: void processSettings(bool load); @@ -164,7 +164,6 @@ private: RsGxsChannelPostsModel *mChannelPostsModel; RsGxsChannelPostFilesModel *mChannelPostFilesModel; RsGxsChannelPostFilesModel *mChannelFilesModel; - RsGxsChannelPostFilesProxyModel *mChannelPostFilesProxyModel ; /* UI - from Designer */ Ui::GxsChannelPostsWidgetWithModel *ui; From d0c5dc4e2496204cf88e0b0c73091485d87be441 Mon Sep 17 00:00:00 2001 From: sehraf Date: Wed, 10 Jun 2020 21:22:45 +0200 Subject: [PATCH 41/89] i2p: bob: convert to RS_DBG --- .../src/services/autoproxy/p3i2pbob.cc | 129 +++++++++--------- 1 file changed, 63 insertions(+), 66 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index ef8e1ad48..c1b32fa76 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -26,7 +26,6 @@ #include "pqi/p3peermgr.h" #include "rsitems/rsconfigitems.h" -#include "util/rsdebug.h" #include "util/radix32.h" #include "util/radix64.h" #include "util/rsdebug.h" @@ -52,8 +51,6 @@ static const int sleepFactorDefault = 10; // 0.5s static const int sleepFactorFast = 1; // 0.05s static const int sleepFactorSlow = 20; // 1s -RS_SET_CONTEXT_DEBUG_LEVEL(0) - static const rstime_t selfCheckPeroid = 30; void doSleep(useconds_t timeToSleepMS) { @@ -83,7 +80,7 @@ bool p3I2pBob::isEnabled() bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) { - std::cout << "p3I2pBob::initialSetup" << std::endl; + RS_DBG("") << std::endl; // update config { @@ -96,7 +93,7 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) } } - std::cout << "p3I2pBob::initialSetup config updated" << std::endl; + RS_DBG("config updated") << std::endl; // request keys // p3I2pBob::stateMachineBOB expects mProcessing to be set therefore @@ -106,12 +103,12 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) fakeTicket->task = autoProxyTask::receiveKey; processTaskAsync(fakeTicket); - std::cout << "p3I2pBob::initialSetup fakeTicket requested" << std::endl; + RS_DBG("fakeTicket requested") << std::endl; // now start thread start("I2P-BOB gen key"); - std::cout << "p3I2pBob::initialSetup thread started" << std::endl; + RS_DBG("thread started") << std::endl; int counter = 0; // wait for keys @@ -125,24 +122,24 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) break; if (++counter > 30) { - std::cout << "p3I2pBob::initialSetup timeout!" << std::endl; + RS_DBG4("timeout!") << std::endl; return false; } } - std::cout << "p3I2pBob::initialSetup got keys" << std::endl; + RS_DBG("got keys") << std::endl; // stop thread fullstop(); - std::cout << "p3I2pBob::initialSetup thread stopped" << std::endl; + RS_DBG("thread stopped") << std::endl; { RS_STACK_MUTEX(mLock); addr = mSetting.address.base32; } - std::cout << "p3I2pBob::initialSetup addr '" << addr << "'" << std::endl; + RS_DBG4("addr '" + addr + "'") << std::endl; return true; } @@ -160,7 +157,7 @@ void p3I2pBob::processTaskAsync(taskTicket *ticket) } break; default: - RsDbg() << __PRETTY_FUNCTION__ << " unknown task" << std::endl; + RS_DBG("unknown task") << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -175,7 +172,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::status: // check if everything needed is set if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::status data is missing" << std::endl; + RS_DBG("autoProxyTask::status data is missing") << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -189,7 +186,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::getSettings: // check if everything needed is set if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::getSettings data is missing" << std::endl; + RS_DBG("autoProxyTask::getSettings data is missing") << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -203,7 +200,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::setSettings: // check if everything needed is set if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::setSettings data is missing" << std::endl; + RS_DBG("autoProxyTask::setSettings data is missing") << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -223,7 +220,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) break; case autoProxyTask::getErrorInfo: if (!data) { - RsDbg() << __PRETTY_FUNCTION__ << " autoProxyTask::getErrorInfo data is missing" << std::endl; + RS_DBG("autoProxyTask::getErrorInfo data is missing") << std::endl; rsAutoProxyMonitor::taskError(ticket); } else { RS_STACK_MUTEX(mLock); @@ -232,7 +229,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) } break; default: - RsDbg() << __PRETTY_FUNCTION__ << " unknown task" << std::endl; + RS_DBG("unknown task") << std::endl; rsAutoProxyMonitor::taskError(ticket); break; } @@ -253,7 +250,7 @@ void p3I2pBob::threadTick() RS_STACK_MUTEX(mLock); std::stringstream ss; ss << "data_tick mState: " << mState << " mTask: " << mTask << " mBOBState: " << mBOBState << " mPending: " << mPending.size(); - Dbg4() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; + RS_DBG4("" + ss.str()) << std::endl; } sleepTime += stateMachineController(); @@ -294,13 +291,13 @@ int p3I2pBob::stateMachineBOB() while (answer.find("OK Listing done") == std::string::npos) { std::stringstream ss; ss << "stateMachineBOB status check: read loop, counter: " << counter; - Dbg3() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; + RS_DBG3("" + ss.str()) << std::endl; answer += recv(); counter++; } if (answer.find(mTunnelName) == std::string::npos) { - RsDbg() << __PRETTY_FUNCTION__ << " status check: tunnel down!" << std::endl; + RS_DBG("status check: tunnel down!") << std::endl; // signal error *((bool *)mProcessing->data) = true; } @@ -340,8 +337,8 @@ int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bo return sleepFactorDefault; } - RsDbg() << __PRETTY_FUNCTION__ << " FAILED to run command '" + currentState.command + "'" << std::endl; - RsDbg() << __PRETTY_FUNCTION__ << " '" + answer + "'" << std::endl; + RS_DBG("FAILED to run command '" + currentState.command + "'") << std::endl; + RS_DBG("'" + answer + "'") << std::endl; mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n'); mErrorMsg.append("reason '" + answer + "'" + '\n'); @@ -388,14 +385,14 @@ int p3I2pBob::stateMachineController() return stateMachineController_locked_idle(); case csDoConnect: if (!connectI2P()) { - RsDbg() << __PRETTY_FUNCTION__ << " doConnect: unable to connect" << std::endl; + RS_DBG("doConnect: unable to connect") << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "unable to connect to BOB port"; return sleepFactorSlow; } - Dbg4() << __PRETTY_FUNCTION__ << " doConnect: connected" << std::endl; + RS_DBG4("doConnect: connected") << std::endl; mState = csConnected; break; case csConnected: @@ -403,7 +400,7 @@ int p3I2pBob::stateMachineController() case csWaitForBob: // check connection problems if (mSocket == 0) { - RsDbg() << __PRETTY_FUNCTION__ << " waitForBob: conection lost" << std::endl; + RS_DBG("waitForBob: conection lost") << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "connection lost to BOB"; @@ -413,21 +410,21 @@ int p3I2pBob::stateMachineController() // check for finished BOB protocol if (mBOBState == bsCleared) { // done - Dbg4() << __PRETTY_FUNCTION__ << " waitForBob: mBOBState == bsCleared" << std::endl; + RS_DBG4("waitForBob: mBOBState == bsCleared") << std::endl; mState = csDoDisconnect; } break; case csDoDisconnect: if (!disconnectI2P() || mSocket != 0) { // just in case - RsDbg() << __PRETTY_FUNCTION__ << " doDisconnect: can't disconnect" << std::endl; + RS_DBG("doDisconnect: can't disconnect") << std::endl; mStateOld = mState; mState = csError; mErrorMsg = "unable to disconnect from BOB"; return sleepFactorDefault; } - Dbg4() << __PRETTY_FUNCTION__ << " doDisconnect: disconnected" << std::endl; + RS_DBG4("doDisconnect: disconnected") << std::endl; mState = csDisconnected; break; case csDisconnected: @@ -458,7 +455,7 @@ int p3I2pBob::stateMachineController_locked_idle() mProcessing->task == autoProxyTask::stop || mProcessing->task == autoProxyTask::proxyStatusCheck)) { // skip since we are not enabled - Dbg1() << __PRETTY_FUNCTION__ << " disabled -> skipping ticket" << std::endl; + RS_DBG1("disabled -> skipping ticket") << std::endl; rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled); mProcessing = NULL; } else { @@ -480,7 +477,7 @@ int p3I2pBob::stateMachineController_locked_idle() mTask = ctRunCheck; break; default: - Dbg1() << __PRETTY_FUNCTION__ << " unknown async task" << std::endl; + RS_DBG1("unknown async task") << std::endl; rsAutoProxyMonitor::taskError(mProcessing); mProcessing = NULL; break; @@ -528,28 +525,28 @@ int p3I2pBob::stateMachineController_locked_connected() case ctRunSetUp: // when we have a key use it for server tunnel! if(mSetting.address.privateKey.empty()) { - Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = setnickC" << std::endl; + RS_DBG4("setting mBOBState = setnickC") << std::endl; mBOBState = bsSetnickC; } else { - Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = setnickS" << std::endl; + RS_DBG4("setting mBOBState = setnickS") << std::endl; mBOBState = bsSetnickS; } break; case ctRunShutDown: // shut down existing tunnel - Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = getnick" << std::endl; + RS_DBG4("setting mBOBState = getnick") << std::endl; mBOBState = bsGetnick; break; case ctRunCheck: - Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = list" << std::endl; + RS_DBG4("setting mBOBState = list") << std::endl; mBOBState = bsList; break; case ctRunGetKeys: - Dbg4() << __PRETTY_FUNCTION__ << " setting mBOBState = setnickN" << std::endl; + RS_DBG4("setting mBOBState = setnickN") << std::endl; mBOBState = bsSetnickN; break; case ctIdle: - RsDbg() << __PRETTY_FUNCTION__ << " task is idle. This should not happen!" << std::endl; + RS_DBG("task is idle. This should not happen!") << std::endl; break; } @@ -565,7 +562,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() if(errorHappened) { // reset old state mStateOld = csIdel; - RsDbg() << __PRETTY_FUNCTION__ << " error during process!" << std::endl; + RS_DBG("error during process!") << std::endl; } // answer ticket @@ -594,12 +591,12 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; if (!errorHappened) { - Dbg4() << __PRETTY_FUNCTION__ << " run check result: ok" << std::endl; + RS_DBG4("run check result: ok") << std::endl; break; } // switch to error newState = csError; - RsDbg() << __PRETTY_FUNCTION__ << " run check result: error" << std::endl; + RS_DBG("run check result: error") << std::endl; mErrorMsg = "Connection check failed. Will try to restart tunnel."; break; @@ -622,7 +619,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; break; case ctIdle: - RsDbg() << __PRETTY_FUNCTION__ << " task is idle. This should not happen!" << std::endl; + RS_DBG("task is idle. This should not happen!") << std::endl; rsAutoProxyMonitor::taskError(mProcessing); } mProcessing = NULL; @@ -638,14 +635,14 @@ int p3I2pBob::stateMachineController_locked_error() { // wait for bob protocoll if (mBOBState != bsCleared) { - Dbg4() << __PRETTY_FUNCTION__ << " waiting for BOB" << std::endl; + RS_DBG4("waiting for BOB") << std::endl; return sleepFactorFast; } #if 0 std::stringstream ss; ss << "stateMachineController_locked_error: mProcessing: " << (mProcessing ? "not null" : "null"); - Dbg4() << __PRETTY_FUNCTION__ << " " + ss.str() << std::endl; + RS_DBG4("" + ss.str()) << std::endl; #endif // try to finish ticket @@ -653,7 +650,7 @@ int p3I2pBob::stateMachineController_locked_error() switch (mTask) { case ctRunCheck: // connection check failed at some point - RsDbg() << __PRETTY_FUNCTION__ << " failed to check proxy status (it's likely dead)!" << std::endl; + RS_DBG("failed to check proxy status (it's likely dead)!") << std::endl; *((bool *)mProcessing->data) = true; mState = csDoDisconnect; mStateOld = csIdel; @@ -661,7 +658,7 @@ int p3I2pBob::stateMachineController_locked_error() break; case ctRunShutDown: // not a big deal though - RsDbg() << __PRETTY_FUNCTION__ << " failed to shut down tunnel (it's likely dead though)!" << std::endl; + RS_DBG("failed to shut down tunnel (it's likely dead though)!") << std::endl; mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); @@ -669,14 +666,14 @@ int p3I2pBob::stateMachineController_locked_error() case ctIdle: // should not happen but we need to deal with it // this will produce some error messages in the log and finish the task (marked as failed) - RsDbg() << __PRETTY_FUNCTION__ << " task is idle. This should not happen!" << std::endl; + RS_DBG("task is idle. This should not happen!") << std::endl; mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); break; case ctRunGetKeys: case ctRunSetUp: - RsDbg() << __PRETTY_FUNCTION__ << " failed to receive key / start up" << std::endl; + RS_DBG("failed to receive key / start up") << std::endl; mStateOld = csError; mState = csDoDisconnect; // keep the error message @@ -687,7 +684,7 @@ int p3I2pBob::stateMachineController_locked_error() // periodically retry if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) { - RsDbg() << __PRETTY_FUNCTION__ << " retrying" << std::endl; + RS_DBG("retrying") << std::endl; mLastProxyCheck = time(NULL); mErrorMsg.clear(); @@ -700,7 +697,7 @@ int p3I2pBob::stateMachineController_locked_error() // check for new tickets if (!mPending.empty()) { - Dbg4() << __PRETTY_FUNCTION__ << " processing new ticket" << std::endl; + RS_DBG4("processing new ticket") << std::endl; // reset and try new task mTask = ctIdle; @@ -731,7 +728,7 @@ RsSerialiser *p3I2pBob::setupSerialiser() bool p3I2pBob::saveList(bool &cleanup, std::list &lst) { - Dbg4() << __PRETTY_FUNCTION__ << std::endl; + RS_DBG4("") << std::endl; cleanup = true; RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; @@ -766,7 +763,7 @@ bool p3I2pBob::saveList(bool &cleanup, std::list &lst) bool p3I2pBob::loadList(std::list &load) { - Dbg4() << __PRETTY_FUNCTION__ << std::endl; + RS_DBG4("") << std::endl; for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { RsConfigKeyValueSet *vitem = dynamic_cast(*it); @@ -786,7 +783,7 @@ bool p3I2pBob::loadList(std::list &load) getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) else - RsDbg() << __PRETTY_FUNCTION__ << " unknown key: " + kit->key << std::endl; + RS_DBG("unknown key: " + kit->key) << std::endl; } } delete vitem; @@ -850,7 +847,7 @@ void p3I2pBob::getStates(bobStates *bs) std::string p3I2pBob::executeCommand(const std::string &command) { - Dbg4() << __PRETTY_FUNCTION__ << " running '" + command + "'" << std::endl; + RS_DBG4("running '" + command + "'") << std::endl; std::string copy = command; copy.push_back('\n'); @@ -862,7 +859,7 @@ std::string p3I2pBob::executeCommand(const std::string &command) // receive answer (trailing new line is already removed!) std::string ans = recv(); - Dbg4() << __PRETTY_FUNCTION__ << " answer '" + ans + "'" << std::endl; + RS_DBG4("answer '" + ans + "'") << std::endl; return ans; } @@ -872,7 +869,7 @@ bool p3I2pBob::connectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket != 0) { - RsDbg() << __PRETTY_FUNCTION__ << " mSocket != 0" << std::endl; + RS_DBG("mSocket != 0") << std::endl; return false; } @@ -880,21 +877,21 @@ bool p3I2pBob::connectI2P() mSocket = unix_socket(PF_INET, SOCK_STREAM, 0); if (mSocket < 0) { - RsDbg() << __PRETTY_FUNCTION__ << " Failed to open socket! Socket Error: " + socket_errorType(errno) << std::endl; + RS_DBG("Failed to open socket! Socket Error: " + socket_errorType(errno)) << std::endl; return false; } // connect int err = unix_connect(mSocket, mI2PProxyAddr); if (err != 0) { - RsDbg() << __PRETTY_FUNCTION__ << " Failed to connect to BOB! Socket Error: " + socket_errorType(errno) << std::endl; + RS_DBG("Failed to connect to BOB! Socket Error: " + socket_errorType(errno)) << std::endl; return false; } // receive hello msg recv(); - Dbg4() << __PRETTY_FUNCTION__ << " done" << std::endl; + RS_DBG4("done") << std::endl; return true; } @@ -903,17 +900,17 @@ bool p3I2pBob::disconnectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket == 0) { - RsDbg() << __PRETTY_FUNCTION__ << " mSocket == 0" << std::endl; + RS_DBG("mSocket == 0") << std::endl; return true; } int err = unix_close(mSocket); if (err != 0) { - RsDbg() << __PRETTY_FUNCTION__ << " Failed to close socket! Socket Error: " + socket_errorType(errno) << std::endl; + RS_DBG("Failed to close socket! Socket Error: " + socket_errorType(errno)) << std::endl; return false; } - Dbg4() << __PRETTY_FUNCTION__ << " done" << std::endl; + RS_DBG4("done") << std::endl; mSocket = 0; return true; } @@ -934,7 +931,7 @@ std::string toString(const std::string &a, const int8_t b) { void p3I2pBob::finalizeSettings_locked() { - Dbg4() << __PRETTY_FUNCTION__ << std::endl; + RS_DBG4("") << std::endl; sockaddr_storage_clear(mI2PProxyAddr); // get i2p proxy addr @@ -945,8 +942,8 @@ void p3I2pBob::finalizeSettings_locked() sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); sockaddr_storage_setport(mI2PProxyAddr, 2827); - Dbg4() << __PRETTY_FUNCTION__ << " using " + sockaddr_storage_tostring(mI2PProxyAddr) << std::endl; - Dbg4() << __PRETTY_FUNCTION__ << " using " + mSetting.address.base32 << std::endl; + RS_DBG4("using " + sockaddr_storage_tostring(mI2PProxyAddr)) << std::endl; + RS_DBG4("using " + mSetting.address.base32) << std::endl; peerState ps; mPeerMgr->getOwnNetStatus(ps); @@ -961,7 +958,7 @@ void p3I2pBob::finalizeSettings_locked() std::vector tmp(len); RSRandom::random_bytes(tmp.data(), len); const std::string location = Radix32::encode(tmp.data(), len); - Dbg4() << __PRETTY_FUNCTION__ << " using suffix " + location << std::endl; + RS_DBG4("using suffix " + location) << std::endl; mTunnelName = "RetroShare-" + location; const std::string setnick = "setnick RetroShare-" + location; @@ -1029,7 +1026,7 @@ void p3I2pBob::finalizeSettings_locked() void p3I2pBob::updateSettings_locked() { - Dbg4() << __PRETTY_FUNCTION__ << std::endl; + RS_DBG4("") << std::endl; sockaddr_storage proxy; mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); @@ -1105,7 +1102,7 @@ std::string p3I2pBob::recv() // sanity check if (length != bufferSize) { // expectation: a full buffer was peeked) - Dbg1() << __PRETTY_FUNCTION__ << " peeked less than bufferSize but also didn't found a new line character" << std::endl; + RS_DBG1("peeked less than bufferSize but also didn't found a new line character") << std::endl; } // this should never happen assert(length <= bufferSize); From 008a4b87b30a04586fc8d5c4e2c12c9e9d10df4d Mon Sep 17 00:00:00 2001 From: sehraf Date: Wed, 10 Jun 2020 21:46:59 +0200 Subject: [PATCH 42/89] replace std::map with std::array --- libretroshare/src/util/i2pcommon.cpp | 8 +++---- libretroshare/src/util/i2pcommon.h | 36 +++++++++++++++------------- 2 files changed, 23 insertions(+), 21 deletions(-) diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index e50fc9d83..46064ee83 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -133,20 +133,20 @@ std::string publicKeyFromPrivate(std::string const &priv) RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " has oversize" << std::endl; // calculate oversize - auto it = signingKeyLengths.find(static_cast(signingKeyType)); - if (it == signingKeyLengths.end()) { + if (signingKeyType >= signingKeyLengths.size()) { // just in case RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " cannot be found in size map!" << std::endl; return std::string(); } - if (it->second.first <= 128) { + auto values = signingKeyLengths[signingKeyType]; + if (values.first <= 128) { // just in case, it's supposed to be larger! RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " is oversize but size calculation would underflow!" << std::endl; return std::string(); } - publicKeyLen += it->second.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted + publicKeyLen += values.first - 128; // 128 = default DSA key length = the space than can be used before the key must be splitted } // Crypto Public Key diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h index 98815496b..63f1ba5af 100644 --- a/libretroshare/src/util/i2pcommon.h +++ b/libretroshare/src/util/i2pcommon.h @@ -161,25 +161,27 @@ enum class CryptoKeyType : uint16_t { X25519 = 4 }; -static const std::map> cryptoKeyLengths { - {CryptoKeyType::ElGamal, {256, 256}}, - {CryptoKeyType::P256, { 64, 32}}, - {CryptoKeyType::P384, { 96, 48}}, - {CryptoKeyType::P521, {132, 66}}, - {CryptoKeyType::X25519, { 32, 32}}, +static const std::array, 5> cryptoKeyLengths { + /*CryptoKeyType::ElGamal*/ std::make_pair(256, 256), + /*CryptoKeyType::P256, */ std::make_pair( 64, 32), + /*CryptoKeyType::P384, */ std::make_pair( 96, 48), + /*CryptoKeyType::P521, */ std::make_pair(132, 66), + /*CryptoKeyType::X25519,*/ std::make_pair( 32, 32), }; -static const std::map> signingKeyLengths { - {SigningKeyType::DSA_SHA1, {128, 128}}, - {SigningKeyType::ECDSA_SHA256_P256, { 64, 32}}, - {SigningKeyType::ECDSA_SHA384_P384, { 96, 48}}, - {SigningKeyType::ECDSA_SHA512_P521, {132, 66}}, - {SigningKeyType::RSA_SHA256_2048, {256, 512}}, - {SigningKeyType::RSA_SHA384_3072, {384, 768}}, - {SigningKeyType::RSA_SHA512_4096, {512,1024}}, - {SigningKeyType::EdDSA_SHA512_Ed25519, { 32, 32}}, - {SigningKeyType::EdDSA_SHA512_Ed25519ph,{ 32, 32}}, - {SigningKeyType::RedDSA_SHA512_Ed25519, { 32, 32}}, +static const std::array, 12> signingKeyLengths { + /*SigningKeyType::DSA_SHA1, */ std::make_pair(128, 128), + /*SigningKeyType::ECDSA_SHA256_P256, */ std::make_pair( 64, 32), + /*SigningKeyType::ECDSA_SHA384_P384, */ std::make_pair( 96, 48), + /*SigningKeyType::ECDSA_SHA512_P521, */ std::make_pair(132, 66), + /*SigningKeyType::RSA_SHA256_2048, */ std::make_pair(256, 512), + /*SigningKeyType::RSA_SHA384_3072, */ std::make_pair(384, 768), + /*SigningKeyType::RSA_SHA512_4096, */ std::make_pair(512,1024), + /*SigningKeyType::EdDSA_SHA512_Ed25519 */ std::make_pair( 32, 32), + /*SigningKeyType::EdDSA_SHA512_Ed25519ph */ std::make_pair( 32, 32), + /*reserved (GOST) */ std::make_pair( 64, 0), + /*reserved (GOST) */ std::make_pair(128, 0), + /*SigningKeyType::RedDSA_SHA512_Ed25519 */ std::make_pair( 32, 32), }; From 7207e6a2c15837ca22dc8e4ae8f34f72053f74f5 Mon Sep 17 00:00:00 2001 From: sehraf Date: Wed, 10 Jun 2020 21:59:45 +0200 Subject: [PATCH 43/89] use RS_DBG --- libretroshare/src/util/i2pcommon.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index 46064ee83..86c83c367 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -73,7 +73,7 @@ std::string publicKeyFromPrivate(std::string const &priv) RsBase64::decode(priv_copy, dataPriv); auto p = dataPriv.cbegin(); - RsDbg() << __PRETTY_FUNCTION__ << " dataPriv.size " << dataPriv.size() << std::endl; + RS_DBG("dataPriv.size ") << dataPriv.size() << std::endl; size_t publicKeyLen = 256 + 128; // default length (bytes) uint8_t certType = 0; @@ -98,12 +98,12 @@ std::string publicKeyFromPrivate(std::string const &priv) * type null is followed by 0x00 0x00 * so has to be 0! */ - RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Null" << std::endl; + RS_DBG("cert is CertType.Null"); publicKeyLen += 3; // add 0x00 0x00 0x00 if (len != 0) // weird - RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Null but len != 0" << std::endl; + RS_DBG("cert is CertType.Null but len != 0"); break; } @@ -111,11 +111,11 @@ std::string publicKeyFromPrivate(std::string const &priv) // check for != 5 if (certType != static_cast::type>(CertType::Key)) { // unsupported - RsDbg() << __PRETTY_FUNCTION__ << " cert type " << certType << " is unsupported" << std::endl; + RS_DBG("cert type ") << certType << " is unsupported" << std::endl; return std::string(); } - RsDbg() << __PRETTY_FUNCTION__ << " cert is CertType.Key" << std::endl; + RS_DBG("cert is CertType.Key"); publicKeyLen += 7; // = 1 + 2 + 2 + 2 = 7 bytes /* @@ -128,21 +128,21 @@ std::string publicKeyFromPrivate(std::string const &priv) // likely 7 signingKeyType = readTwoBytesBE(p); - RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << std::endl; + RS_DBG("signing pubkey type ") << certType << std::endl; if (signingKeyType >= 3 && signingKeyType <= 6) { - RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " has oversize" << std::endl; + RS_DBG("signing pubkey type ") << certType << " has oversize" << std::endl; // calculate oversize if (signingKeyType >= signingKeyLengths.size()) { // just in case - RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " cannot be found in size map!" << std::endl; + RS_DBG("signing pubkey type ") << certType << " cannot be found in size map!" << std::endl; return std::string(); } auto values = signingKeyLengths[signingKeyType]; if (values.first <= 128) { // just in case, it's supposed to be larger! - RsDbg() << __PRETTY_FUNCTION__ << " signing pubkey type " << certType << " is oversize but size calculation would underflow!" << std::endl; + RS_DBG("signing pubkey type ") << certType << " is oversize but size calculation would underflow!" << std::endl; return std::string(); } @@ -152,12 +152,12 @@ std::string publicKeyFromPrivate(std::string const &priv) // Crypto Public Key // likely 0 cryptKey = readTwoBytesBE(p); - RsDbg() << __PRETTY_FUNCTION__ << " crypto pubkey type " << cryptKey << std::endl; + RS_DBG("crypto pubkey type ") << cryptKey << std::endl; // info: these are all smaller than the default 256 bytes, so no oversize calculation is needed break; } catch (const std::out_of_range &e) { - RsDbg() << __PRETTY_FUNCTION__ << " hit exception!" << e.what() << std::endl; + RS_DBG("hit exception! ") << e.what() << std::endl; return std::string(); } } while(false); From ebbdc082c0b4a5dc75276e580bdda73699b32c8f Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 10 Jun 2020 23:16:32 +0200 Subject: [PATCH 44/89] implemented sorting manually in channel files lists --- .../gxschannels/GxsChannelPostFilesModel.cpp | 37 +++++++++++++++++++ .../gxschannels/GxsChannelPostFilesModel.h | 1 + .../GxsChannelPostsWidgetWithModel.cpp | 18 ++++++--- .../GxsChannelPostsWidgetWithModel.h | 5 ++- 4 files changed, 53 insertions(+), 8 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 86dc330cc..6b4a4b14d 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -370,6 +370,43 @@ void RsGxsChannelPostFilesModel::setFilter(const QStringList& strings, uint32_t& postMods(); } + +class compareOperator +{ +public: + compareOperator(int column,Qt::SortOrder order): col(column),ord(order) {} + + bool operator()(const RsGxsFile& f1,const RsGxsFile& f2) const + { + switch(col) + { + default: + case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return (ord==Qt::AscendingOrder)?(f1.mNamef2.mName); + case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return (ord==Qt::AscendingOrder)?(f1.mSizef2.mSize); + case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: + { + FileInfo fi1,fi2; + rsFiles->FileDetails(f1.mHash,RS_FILE_HINTS_DOWNLOAD,fi1); + rsFiles->FileDetails(f2.mHash,RS_FILE_HINTS_DOWNLOAD,fi2); + + return (ord==Qt::AscendingOrder)?(fi1.transferedfi2.transfered); + } + } + + } + +private: + int col; + Qt::SortOrder ord; +}; + +void RsGxsChannelPostFilesModel::sort(int column, Qt::SortOrder order) +{ + std::sort(mFiles.begin(),mFiles.end(),compareOperator(column,order)); + + update(); +} + #ifdef TODO QVariant RsGxsForumModel::textColorRole(const ForumModelPostEntry& fmpe,int /*column*/) const { diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index 17563fa6d..4f0e432ff 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -90,6 +90,7 @@ public: int rowCount(const QModelIndex& parent = QModelIndex()) const override; int columnCount(const QModelIndex &parent = QModelIndex()) const override; bool hasChildren(const QModelIndex &parent = QModelIndex()) const override; + void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override; QModelIndex index(int row, int column, const QModelIndex & parent = QModelIndex()) const override; QModelIndex parent(const QModelIndex& child) const override; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index d7b565e0b..c9d0a882a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -302,7 +302,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->channelFiles_TV->setSortingEnabled(true); ui->channelFiles_TV->sortByColumn(0, Qt::AscendingOrder); - //connect(ui->channelPostFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumn(int,Qt::SortOrder))); + connect(ui->channelPostFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumnPostFiles(int,Qt::SortOrder))); + connect(ui->channelFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumnFiles(int,Qt::SortOrder))); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); connect(mChannelPostsModel,SIGNAL(channelLoaded()),this,SLOT(updateChannelFiles())); @@ -365,11 +366,16 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI }, mEventHandlerId, RsEventType::GXS_CHANNELS ); } -//void GxsChannelPostsWidgetWithModel::sortColumn(int col,Qt::SortOrder so) -//{ -// std::cerr << "Sorting!!"<< std::endl; -// mChannelPostFilesProxyModel->sort(col,so); -//} +void GxsChannelPostsWidgetWithModel::sortColumnPostFiles(int col,Qt::SortOrder so) +{ + std::cerr << "Sorting post files according to col " << col << std::endl; + mChannelPostFilesModel->sort(col,so); +} +void GxsChannelPostsWidgetWithModel::sortColumnFiles(int col,Qt::SortOrder so) +{ + std::cerr << "Sorting channel files according to col " << col << std::endl; + mChannelFilesModel->sort(col,so); +} void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s) { diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index cf7ead667..90c1f76ad 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -140,8 +140,9 @@ private slots: void handlePostsTreeSizeChange(QSize s); void updateChannelFiles(); -// public slots: -// void sortColumn(int col,Qt::SortOrder so); +public slots: + void sortColumnFiles(int col,Qt::SortOrder so); + void sortColumnPostFiles(int col,Qt::SortOrder so); private: void processSettings(bool load); From 86c30a01dd5fb20e1474ec6a859ee72d8306cc40 Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 11 Jun 2020 10:45:33 +0200 Subject: [PATCH 45/89] next attampt to use RS_DBG correctly --- .../src/services/autoproxy/p3i2pbob.cc | 136 +++++++++--------- libretroshare/src/util/i2pcommon.cpp | 16 +-- 2 files changed, 74 insertions(+), 78 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index c1b32fa76..02b951397 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -33,6 +33,8 @@ #include "util/rsprint.h" #include "util/rsrandom.h" +#include "util/rsdebuglevel4.h" + static const std::string kConfigKeyBOBEnable = "BOB_ENABLE"; static const std::string kConfigKeyBOBKey = "BOB_KEY"; static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; @@ -80,7 +82,7 @@ bool p3I2pBob::isEnabled() bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) { - RS_DBG("") << std::endl; + RS_DBG(""); // update config { @@ -93,7 +95,7 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) } } - RS_DBG("config updated") << std::endl; + RS_DBG("config updated"); // request keys // p3I2pBob::stateMachineBOB expects mProcessing to be set therefore @@ -103,12 +105,12 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) fakeTicket->task = autoProxyTask::receiveKey; processTaskAsync(fakeTicket); - RS_DBG("fakeTicket requested") << std::endl; + RS_DBG("fakeTicket requested"); // now start thread start("I2P-BOB gen key"); - RS_DBG("thread started") << std::endl; + RS_DBG("thread started"); int counter = 0; // wait for keys @@ -122,24 +124,24 @@ bool p3I2pBob::initialSetup(std::string &addr, uint16_t &/*port*/) break; if (++counter > 30) { - RS_DBG4("timeout!") << std::endl; + RS_DBG4("timeout!"); return false; } } - RS_DBG("got keys") << std::endl; + RS_DBG("got keys"); // stop thread fullstop(); - RS_DBG("thread stopped") << std::endl; + RS_DBG("thread stopped"); { RS_STACK_MUTEX(mLock); addr = mSetting.address.base32; } - RS_DBG4("addr '" + addr + "'") << std::endl; + RS_DBG4("addr ", addr); return true; } @@ -157,7 +159,7 @@ void p3I2pBob::processTaskAsync(taskTicket *ticket) } break; default: - RS_DBG("unknown task") << std::endl; + RS_DBG("unknown task"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -172,7 +174,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::status: // check if everything needed is set if (!data) { - RS_DBG("autoProxyTask::status data is missing") << std::endl; + RS_DBG("autoProxyTask::status data is missing"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -186,7 +188,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::getSettings: // check if everything needed is set if (!data) { - RS_DBG("autoProxyTask::getSettings data is missing") << std::endl; + RS_DBG("autoProxyTask::getSettings data is missing"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -200,7 +202,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) case autoProxyTask::setSettings: // check if everything needed is set if (!data) { - RS_DBG("autoProxyTask::setSettings data is missing") << std::endl; + RS_DBG("autoProxyTask::setSettings data is missing"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -220,7 +222,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) break; case autoProxyTask::getErrorInfo: if (!data) { - RS_DBG("autoProxyTask::getErrorInfo data is missing") << std::endl; + RS_DBG("autoProxyTask::getErrorInfo data is missing"); rsAutoProxyMonitor::taskError(ticket); } else { RS_STACK_MUTEX(mLock); @@ -229,7 +231,7 @@ void p3I2pBob::processTaskSync(taskTicket *ticket) } break; default: - RS_DBG("unknown task") << std::endl; + RS_DBG("unknown task"); rsAutoProxyMonitor::taskError(ticket); break; } @@ -247,10 +249,8 @@ void p3I2pBob::threadTick() { int sleepTime = 0; { - RS_STACK_MUTEX(mLock); - std::stringstream ss; - ss << "data_tick mState: " << mState << " mTask: " << mTask << " mBOBState: " << mBOBState << " mPending: " << mPending.size(); - RS_DBG4("" + ss.str()) << std::endl; + RS_STACK_MUTEX(mLock); + RS_DBG4("data_tick mState: ", mState, " mTask: ", mTask, " mBOBState: ", mBOBState, " mPending: ", mPending.size()); } sleepTime += stateMachineController(); @@ -289,15 +289,13 @@ int p3I2pBob::stateMachineBOB() if (mBOBState == bsList) { int counter = 0; while (answer.find("OK Listing done") == std::string::npos) { - std::stringstream ss; - ss << "stateMachineBOB status check: read loop, counter: " << counter; - RS_DBG3("" + ss.str()) << std::endl; + RS_DBG3("stateMachineBOB status check: read loop, counter: ", counter); answer += recv(); counter++; } if (answer.find(mTunnelName) == std::string::npos) { - RS_DBG("status check: tunnel down!") << std::endl; + RS_DBG("status check: tunnel down!"); // signal error *((bool *)mProcessing->data) = true; } @@ -337,8 +335,8 @@ int p3I2pBob::stateMachineBOB_locked_failure(const std::string &answer, const bo return sleepFactorDefault; } - RS_DBG("FAILED to run command '" + currentState.command + "'") << std::endl; - RS_DBG("'" + answer + "'") << std::endl; + RS_DBG("FAILED to run command: ", currentState.command); + RS_DBG("answer: ", answer); mErrorMsg.append("FAILED to run command '" + currentState.command + "'" + '\n'); mErrorMsg.append("reason '" + answer + "'" + '\n'); @@ -385,14 +383,14 @@ int p3I2pBob::stateMachineController() return stateMachineController_locked_idle(); case csDoConnect: if (!connectI2P()) { - RS_DBG("doConnect: unable to connect") << std::endl; + RS_DBG("doConnect: unable to connect"); mStateOld = mState; mState = csError; mErrorMsg = "unable to connect to BOB port"; return sleepFactorSlow; } - RS_DBG4("doConnect: connected") << std::endl; + RS_DBG4("doConnect: connected"); mState = csConnected; break; case csConnected: @@ -400,7 +398,7 @@ int p3I2pBob::stateMachineController() case csWaitForBob: // check connection problems if (mSocket == 0) { - RS_DBG("waitForBob: conection lost") << std::endl; + RS_DBG("waitForBob: conection lost"); mStateOld = mState; mState = csError; mErrorMsg = "connection lost to BOB"; @@ -410,21 +408,21 @@ int p3I2pBob::stateMachineController() // check for finished BOB protocol if (mBOBState == bsCleared) { // done - RS_DBG4("waitForBob: mBOBState == bsCleared") << std::endl; + RS_DBG4("waitForBob: mBOBState == bsCleared"); mState = csDoDisconnect; } break; case csDoDisconnect: if (!disconnectI2P() || mSocket != 0) { // just in case - RS_DBG("doDisconnect: can't disconnect") << std::endl; + RS_DBG("doDisconnect: can't disconnect"); mStateOld = mState; mState = csError; mErrorMsg = "unable to disconnect from BOB"; return sleepFactorDefault; } - RS_DBG4("doDisconnect: disconnected") << std::endl; + RS_DBG4("doDisconnect: disconnected"); mState = csDisconnected; break; case csDisconnected: @@ -455,7 +453,7 @@ int p3I2pBob::stateMachineController_locked_idle() mProcessing->task == autoProxyTask::stop || mProcessing->task == autoProxyTask::proxyStatusCheck)) { // skip since we are not enabled - RS_DBG1("disabled -> skipping ticket") << std::endl; + RS_DBG1("disabled -> skipping ticket"); rsAutoProxyMonitor::taskDone(mProcessing, autoProxyStatus::disabled); mProcessing = NULL; } else { @@ -477,7 +475,7 @@ int p3I2pBob::stateMachineController_locked_idle() mTask = ctRunCheck; break; default: - RS_DBG1("unknown async task") << std::endl; + RS_DBG1("unknown async task"); rsAutoProxyMonitor::taskError(mProcessing); mProcessing = NULL; break; @@ -525,28 +523,28 @@ int p3I2pBob::stateMachineController_locked_connected() case ctRunSetUp: // when we have a key use it for server tunnel! if(mSetting.address.privateKey.empty()) { - RS_DBG4("setting mBOBState = setnickC") << std::endl; + RS_DBG4("setting mBOBState = setnickC"); mBOBState = bsSetnickC; } else { - RS_DBG4("setting mBOBState = setnickS") << std::endl; + RS_DBG4("setting mBOBState = setnickS"); mBOBState = bsSetnickS; } break; case ctRunShutDown: // shut down existing tunnel - RS_DBG4("setting mBOBState = getnick") << std::endl; + RS_DBG4("setting mBOBState = getnick"); mBOBState = bsGetnick; break; case ctRunCheck: - RS_DBG4("setting mBOBState = list") << std::endl; + RS_DBG4("setting mBOBState = list"); mBOBState = bsList; break; case ctRunGetKeys: - RS_DBG4("setting mBOBState = setnickN") << std::endl; + RS_DBG4("setting mBOBState = setnickN"); mBOBState = bsSetnickN; break; case ctIdle: - RS_DBG("task is idle. This should not happen!") << std::endl; + RS_DBG("task is idle. This should not happen!"); break; } @@ -562,7 +560,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() if(errorHappened) { // reset old state mStateOld = csIdel; - RS_DBG("error during process!") << std::endl; + RS_DBG("error during process!"); } // answer ticket @@ -591,12 +589,12 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; if (!errorHappened) { - RS_DBG4("run check result: ok") << std::endl; + RS_DBG4("run check result: ok"); break; } // switch to error newState = csError; - RS_DBG("run check result: error") << std::endl; + RS_DBG("run check result: error"); mErrorMsg = "Connection check failed. Will try to restart tunnel."; break; @@ -619,7 +617,7 @@ int p3I2pBob::stateMachineController_locked_disconnected() mTask = mTaskOld; break; case ctIdle: - RS_DBG("task is idle. This should not happen!") << std::endl; + RS_DBG("task is idle. This should not happen!"); rsAutoProxyMonitor::taskError(mProcessing); } mProcessing = NULL; @@ -635,14 +633,12 @@ int p3I2pBob::stateMachineController_locked_error() { // wait for bob protocoll if (mBOBState != bsCleared) { - RS_DBG4("waiting for BOB") << std::endl; + RS_DBG4("waiting for BOB"); return sleepFactorFast; } #if 0 - std::stringstream ss; - ss << "stateMachineController_locked_error: mProcessing: " << (mProcessing ? "not null" : "null"); - RS_DBG4("" + ss.str()) << std::endl; + RS_DBG4("stateMachineController_locked_error: mProcessing: ", (mProcessing ? "not null" : "null")); #endif // try to finish ticket @@ -650,7 +646,7 @@ int p3I2pBob::stateMachineController_locked_error() switch (mTask) { case ctRunCheck: // connection check failed at some point - RS_DBG("failed to check proxy status (it's likely dead)!") << std::endl; + RS_DBG("failed to check proxy status (it's likely dead)!"); *((bool *)mProcessing->data) = true; mState = csDoDisconnect; mStateOld = csIdel; @@ -658,7 +654,7 @@ int p3I2pBob::stateMachineController_locked_error() break; case ctRunShutDown: // not a big deal though - RS_DBG("failed to shut down tunnel (it's likely dead though)!") << std::endl; + RS_DBG("failed to shut down tunnel (it's likely dead though)!"); mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); @@ -666,14 +662,14 @@ int p3I2pBob::stateMachineController_locked_error() case ctIdle: // should not happen but we need to deal with it // this will produce some error messages in the log and finish the task (marked as failed) - RS_DBG("task is idle. This should not happen!") << std::endl; + RS_DBG("task is idle. This should not happen!"); mState = csDoDisconnect; mStateOld = csIdel; mErrorMsg.clear(); break; case ctRunGetKeys: case ctRunSetUp: - RS_DBG("failed to receive key / start up") << std::endl; + RS_DBG("failed to receive key / start up"); mStateOld = csError; mState = csDoDisconnect; // keep the error message @@ -684,7 +680,7 @@ int p3I2pBob::stateMachineController_locked_error() // periodically retry if (mLastProxyCheck < time(NULL) - (selfCheckPeroid >> 1) && mTask == ctRunSetUp) { - RS_DBG("retrying") << std::endl; + RS_DBG("retrying"); mLastProxyCheck = time(NULL); mErrorMsg.clear(); @@ -697,7 +693,7 @@ int p3I2pBob::stateMachineController_locked_error() // check for new tickets if (!mPending.empty()) { - RS_DBG4("processing new ticket") << std::endl; + RS_DBG4("processing new ticket"); // reset and try new task mTask = ctIdle; @@ -728,7 +724,7 @@ RsSerialiser *p3I2pBob::setupSerialiser() bool p3I2pBob::saveList(bool &cleanup, std::list &lst) { - RS_DBG4("") << std::endl; + RS_DBG4(""); cleanup = true; RsConfigKeyValueSet *vitem = new RsConfigKeyValueSet; @@ -763,7 +759,7 @@ bool p3I2pBob::saveList(bool &cleanup, std::list &lst) bool p3I2pBob::loadList(std::list &load) { - RS_DBG4("") << std::endl; + RS_DBG4(""); for(std::list::const_iterator it = load.begin(); it!=load.end(); ++it) { RsConfigKeyValueSet *vitem = dynamic_cast(*it); @@ -783,7 +779,7 @@ bool p3I2pBob::loadList(std::list &load) getKVSUInt(kit, kConfigKeyOutQuantity, mSetting.outQuantity) getKVSUInt(kit, kConfigKeyOutVariance, mSetting.outVariance) else - RS_DBG("unknown key: " + kit->key) << std::endl; + RS_DBG("unknown key: ", kit->key); } } delete vitem; @@ -847,7 +843,7 @@ void p3I2pBob::getStates(bobStates *bs) std::string p3I2pBob::executeCommand(const std::string &command) { - RS_DBG4("running '" + command + "'") << std::endl; + RS_DBG4("running: ", command); std::string copy = command; copy.push_back('\n'); @@ -859,7 +855,7 @@ std::string p3I2pBob::executeCommand(const std::string &command) // receive answer (trailing new line is already removed!) std::string ans = recv(); - RS_DBG4("answer '" + ans + "'") << std::endl; + RS_DBG4("answer: ", ans); return ans; } @@ -869,7 +865,7 @@ bool p3I2pBob::connectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket != 0) { - RS_DBG("mSocket != 0") << std::endl; + RS_DBG("mSocket != 0"); return false; } @@ -877,21 +873,21 @@ bool p3I2pBob::connectI2P() mSocket = unix_socket(PF_INET, SOCK_STREAM, 0); if (mSocket < 0) { - RS_DBG("Failed to open socket! Socket Error: " + socket_errorType(errno)) << std::endl; + RS_DBG("Failed to open socket! Socket Error: ", socket_errorType(errno)); return false; } // connect int err = unix_connect(mSocket, mI2PProxyAddr); if (err != 0) { - RS_DBG("Failed to connect to BOB! Socket Error: " + socket_errorType(errno)) << std::endl; + RS_DBG("Failed to connect to BOB! Socket Error: ", socket_errorType(errno)); return false; } // receive hello msg recv(); - RS_DBG4("done") << std::endl; + RS_DBG4("done"); return true; } @@ -900,17 +896,17 @@ bool p3I2pBob::disconnectI2P() // there is only one thread that touches mSocket - no need for a lock if (mSocket == 0) { - RS_DBG("mSocket == 0") << std::endl; + RS_DBG("mSocket == 0"); return true; } int err = unix_close(mSocket); if (err != 0) { - RS_DBG("Failed to close socket! Socket Error: " + socket_errorType(errno)) << std::endl; + RS_DBG("Failed to close socket! Socket Error: ", socket_errorType(errno)); return false; } - RS_DBG4("done") << std::endl; + RS_DBG4("done"); mSocket = 0; return true; } @@ -931,7 +927,7 @@ std::string toString(const std::string &a, const int8_t b) { void p3I2pBob::finalizeSettings_locked() { - RS_DBG4("") << std::endl; + RS_DBG4(""); sockaddr_storage_clear(mI2PProxyAddr); // get i2p proxy addr @@ -942,8 +938,8 @@ void p3I2pBob::finalizeSettings_locked() sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); sockaddr_storage_setport(mI2PProxyAddr, 2827); - RS_DBG4("using " + sockaddr_storage_tostring(mI2PProxyAddr)) << std::endl; - RS_DBG4("using " + mSetting.address.base32) << std::endl; + RS_DBG4("using ", sockaddr_storage_tostring(mI2PProxyAddr)); + RS_DBG4("using ", mSetting.address.base32); peerState ps; mPeerMgr->getOwnNetStatus(ps); @@ -958,7 +954,7 @@ void p3I2pBob::finalizeSettings_locked() std::vector tmp(len); RSRandom::random_bytes(tmp.data(), len); const std::string location = Radix32::encode(tmp.data(), len); - RS_DBG4("using suffix " + location) << std::endl; + RS_DBG4("using suffix ", location); mTunnelName = "RetroShare-" + location; const std::string setnick = "setnick RetroShare-" + location; @@ -1026,7 +1022,7 @@ void p3I2pBob::finalizeSettings_locked() void p3I2pBob::updateSettings_locked() { - RS_DBG4("") << std::endl; + RS_DBG4(""); sockaddr_storage proxy; mPeerMgr->getProxyServerAddress(RS_HIDDEN_TYPE_I2P, proxy); @@ -1102,7 +1098,7 @@ std::string p3I2pBob::recv() // sanity check if (length != bufferSize) { // expectation: a full buffer was peeked) - RS_DBG1("peeked less than bufferSize but also didn't found a new line character") << std::endl; + RS_DBG1("peeked less than bufferSize but also didn't found a new line character"); } // this should never happen assert(length <= bufferSize); diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index 86c83c367..eedb9596d 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -73,7 +73,7 @@ std::string publicKeyFromPrivate(std::string const &priv) RsBase64::decode(priv_copy, dataPriv); auto p = dataPriv.cbegin(); - RS_DBG("dataPriv.size ") << dataPriv.size() << std::endl; + RS_DBG("dataPriv.size ", dataPriv.size()); size_t publicKeyLen = 256 + 128; // default length (bytes) uint8_t certType = 0; @@ -111,7 +111,7 @@ std::string publicKeyFromPrivate(std::string const &priv) // check for != 5 if (certType != static_cast::type>(CertType::Key)) { // unsupported - RS_DBG("cert type ") << certType << " is unsupported" << std::endl; + RS_DBG("cert type ", certType, " is unsupported"); return std::string(); } @@ -128,21 +128,21 @@ std::string publicKeyFromPrivate(std::string const &priv) // likely 7 signingKeyType = readTwoBytesBE(p); - RS_DBG("signing pubkey type ") << certType << std::endl; + RS_DBG("signing pubkey type ", certType); if (signingKeyType >= 3 && signingKeyType <= 6) { - RS_DBG("signing pubkey type ") << certType << " has oversize" << std::endl; + RS_DBG("signing pubkey type ", certType, " has oversize"); // calculate oversize if (signingKeyType >= signingKeyLengths.size()) { // just in case - RS_DBG("signing pubkey type ") << certType << " cannot be found in size map!" << std::endl; + RS_DBG("signing pubkey type ", certType, " cannot be found in size data!"); return std::string(); } auto values = signingKeyLengths[signingKeyType]; if (values.first <= 128) { // just in case, it's supposed to be larger! - RS_DBG("signing pubkey type ") << certType << " is oversize but size calculation would underflow!" << std::endl; + RS_DBG("signing pubkey type ", certType, " is oversize but size calculation would underflow!"); return std::string(); } @@ -152,12 +152,12 @@ std::string publicKeyFromPrivate(std::string const &priv) // Crypto Public Key // likely 0 cryptKey = readTwoBytesBE(p); - RS_DBG("crypto pubkey type ") << cryptKey << std::endl; + RS_DBG("crypto pubkey type ", cryptKey); // info: these are all smaller than the default 256 bytes, so no oversize calculation is needed break; } catch (const std::out_of_range &e) { - RS_DBG("hit exception! ") << e.what() << std::endl; + RS_DBG("hit exception! ", e.what()); return std::string(); } } while(false); From 3d784e88714d5282f8f0fe01ee890ae0e0f3c152 Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 11 Jun 2020 11:03:08 +0200 Subject: [PATCH 46/89] remove unnecessary std::replace --- libretroshare/src/services/autoproxy/p3i2pbob.cc | 2 -- libretroshare/src/util/i2pcommon.cpp | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index 02b951397..7fd7594b9 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -33,8 +33,6 @@ #include "util/rsprint.h" #include "util/rsrandom.h" -#include "util/rsdebuglevel4.h" - static const std::string kConfigKeyBOBEnable = "BOB_ENABLE"; static const std::string kConfigKeyBOBKey = "BOB_KEY"; static const std::string kConfigKeyBOBAddr = "BOB_ADDR"; diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index eedb9596d..8c6f2238b 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -19,7 +19,8 @@ std::string keyToBase32Addr(const std::string &key) // replace I2P specific chars std::replace(copy.begin(), copy.end(), '~', '/'); - std::replace(copy.begin(), copy.end(), '-', '+'); + // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too + // std::replace(copy.begin(), copy.end(), '-', '+'); // decode std::vector bin; From 591d2ad8646758465df380bdc9e0c01de97b094f Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 11 Jun 2020 11:15:09 +0200 Subject: [PATCH 47/89] autoproxy: use new RS_DBG --- .../services/autoproxy/rsautoproxymonitor.cc | 24 +++++++++---------- 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index 62b47dfea..3c52cfdc5 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -44,7 +44,7 @@ void rsAutoProxyMonitor::addProxy(autoProxyType::autoProxyType_enum type, autoPr { RS_STACK_MUTEX(mLock); if (mProxies.find(type) != mProxies.end()) { - RsErr() << __PRETTY_FUNCTION__<< " type " << type << " already added - OVERWRITING" << std::endl; + RS_ERR("type ", type, " already added - OVERWRITING"); print_stacktrace(); } @@ -120,7 +120,7 @@ void rsAutoProxyMonitor::stopAllRSShutdown() do { rstime::rs_usleep(1000 * 1000); RS_STACK_MUTEX(mLock); - RsDbg() << __PRETTY_FUNCTION__<< " waiting for auto proxy service(s) to shut down " << t << "/" << timeout << " (remaining: " << mProxies.size() << ")" << std::endl; + RS_DBG("waiting for auto proxy service(s) to shut down ", t, "/", timeout, " (remaining: ", mProxies.size(), ")"); if (mProxies.empty()) break; t++; @@ -149,15 +149,15 @@ void rsAutoProxyMonitor::task(taskTicket *ticket) { // sanity checks if (!ticket->async && ticket->types.size() > 1) { - RsErr() << __PRETTY_FUNCTION__<< " synchronous call to multiple services. This can cause problems!" << std::endl; + RS_ERR("synchronous call to multiple services. This can cause problems!"); print_stacktrace(); } if (ticket->async && !ticket->cb && ticket->data) { - RsErr() << __PRETTY_FUNCTION__<< " asynchronous call with data but no callback. This will likely causes memory leak!" << std::endl; + RS_ERR("asynchronous call with data but no callback. This will likely causes memory leak!"); print_stacktrace(); } if (ticket->types.size() > 1 && ticket->data) { - RsErr() << __PRETTY_FUNCTION__<< " call with data to multiple services. This will likely causes memory leak!" << std::endl; + RS_ERR("call with data to multiple services. This will likely causes memory leak!"); print_stacktrace(); } @@ -197,7 +197,7 @@ void rsAutoProxyMonitor::taskAsync(std::vector if (isAsyncTask(task)) { // Usually the services will reject this ticket. // Just print a warning - maybe there is some special case where this is a good idea. - RsErr() << __PRETTY_FUNCTION__<< " called with an asynchronous task!" << std::endl; + RS_ERR("called with an asynchronous task!"); print_stacktrace(); } @@ -256,7 +256,7 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu t->cb->taskFinished(t); if (t != NULL) { // callack did not clean up properly - RsErr() << __PRETTY_FUNCTION__<< " callback did not clean up!" << std::endl; + RS_ERR("callback did not clean up!"); print_stacktrace(); cleanUp = true; } @@ -265,12 +265,12 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu // we must take care of deleting cleanUp = true; if(t->data) - RsErr() << __PRETTY_FUNCTION__<< " async call with data attached but no callback set!" << std::endl; + RS_ERR("sync call with data attached but no callback set!"); } if (cleanUp) { if (t->data) { - RsErr() << __PRETTY_FUNCTION__<< " will try to delete void pointer!" << std::endl; + RS_ERR("will try to delete void pointer!"); print_stacktrace(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-incomplete" @@ -304,7 +304,7 @@ void rsAutoProxyMonitor::taskFinished(taskTicket *&ticket) // clean up if (ticket->data) { - RsErr() << __PRETTY_FUNCTION__<< " data set. Will try to delete void pointer" << std::endl; + RS_ERR(" data set. Will try to delete void pointer"); print_stacktrace(); #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wdelete-incomplete" @@ -323,7 +323,7 @@ autoProxyService *rsAutoProxyMonitor::lookUpService(autoProxyType::autoProxyType if ((itService = mProxies.find(t)) != mProxies.end()) { return itService->second; } - RsDbg() << __PRETTY_FUNCTION__<< " no service for type " << t << " found!" << std::endl; + RS_DBG("no service for type ", t, " found!"); return NULL; } From ebc5a116b22c28f59cf1f584c5a0afe020986550 Mon Sep 17 00:00:00 2001 From: sehraf Date: Thu, 11 Jun 2020 11:18:52 +0200 Subject: [PATCH 48/89] remove unnecessary std::replace --- libretroshare/src/util/i2pcommon.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index 8c6f2238b..575b0fec0 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -67,7 +67,8 @@ std::string publicKeyFromPrivate(std::string const &priv) // creat a copy to work on, need to convert it to standard base64 auto priv_copy(priv); std::replace(priv_copy.begin(), priv_copy.end(), '~', '/'); - std::replace(priv_copy.begin(), priv_copy.end(), '-', '+'); + // replacing the - with a + is not necessary, as RsBase64 can handle base64url encoding, too + // std::replace(copy.begin(), copy.end(), '-', '+'); // get raw data std::vector dataPriv; From 92379b35344a439a133c192b1775458ca42fd94e Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 11 Jun 2020 16:32:55 +0200 Subject: [PATCH 49/89] Added open file feature for file status widget * Added open file feature for file status widget --- .../GxsChannelFilesStatusWidget.cpp | 70 +++++++++++++++--- .../gxschannels/GxsChannelFilesStatusWidget.h | 1 + .../GxsChannelFilesStatusWidget.ui | 22 +++++- retroshare-gui/src/gui/icons.qrc | 1 + retroshare-gui/src/gui/icons/png/arrow.png | Bin 0 -> 935 bytes 5 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 retroshare-gui/src/gui/icons/png/arrow.png diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp index c28601fed..bc019e426 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include #include #include #include @@ -27,6 +28,8 @@ #include "GxsChannelFilesStatusWidget.h" #include "ui_GxsChannelFilesStatusWidget.h" #include "gui/common/RsUrlHandler.h" +#include "gui/common/FilesDefs.h" +#include "util/misc.h" #include "retroshare/rsfiles.h" @@ -40,11 +43,21 @@ GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, setSize(mFile.mSize); /* Connect signals */ - connect(ui->downloadToolButton, SIGNAL(clicked()), this, SLOT(download())); + connect(ui->downloadPushButton, SIGNAL(clicked()), this, SLOT(download())); connect(ui->resumeToolButton, SIGNAL(clicked()), this, SLOT(resume())); connect(ui->pauseToolButton, SIGNAL(clicked()), this, SLOT(pause())); connect(ui->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel())); - connect(ui->openFolderToolButton, SIGNAL(clicked()), this, SLOT(openFolder())); + connect(ui->openFilePushButton, SIGNAL(clicked()), this, SLOT(openFile())); + + ui->downloadPushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/download.png")); + ui->openFolderToolButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/arrow.png")); + + QAction *openfolder = new QAction(tr("Open folder"), this); + connect(openfolder, SIGNAL(triggered()), this, SLOT(openFolder())); + + QMenu *menu = new QMenu(); + menu->addAction(openfolder); + ui->openFolderToolButton->setMenu(menu); check(); } @@ -80,6 +93,17 @@ void GxsChannelFilesStatusWidget::check() if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) { mState = STATE_LOCAL; setSize(fileInfo.size); + + /* check if the file is a media file */ + if (!misc::isPreviewable(QFileInfo(QString::fromUtf8(fileInfo.path.c_str())).suffix())) + { + /* check if the file is not a media file and change text */ + ui->openFilePushButton->setText(tr("Open file")); + } else { + ui->openFilePushButton->setText(tr("Play")); + ui->openFilePushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/play.png")); + } + } else { FileInfo fileInfo; bool detailsOk = rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo); @@ -126,11 +150,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_ERROR: repeat = 0; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Error"); @@ -140,11 +165,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_REMOTE: repeat = 30000; - ui->downloadToolButton->show(); + ui->downloadPushButton->show(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); break; @@ -152,11 +178,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_DOWNLOAD: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->show(); ui->cancelToolButton->show(); ui->progressBar->show(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); break; @@ -164,11 +191,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_PAUSED: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->show(); ui->pauseToolButton->hide(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Paused"); @@ -178,11 +206,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_WAITING: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->show(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Waiting"); @@ -192,11 +221,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_CHECKING: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Checking"); @@ -206,11 +236,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_LOCAL: repeat = 60000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->show(); ui->openFolderToolButton->show(); break; @@ -296,3 +327,24 @@ void GxsChannelFilesStatusWidget::openFolder() } } } + +void GxsChannelFilesStatusWidget::openFile() +{ + FileInfo fileInfo; + if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) { + return; + } + + /* open file with a suitable application */ + QFileInfo qinfo; + qinfo.setFile(QString::fromUtf8(fileInfo.path.c_str())); + if (qinfo.exists()) { + if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) { + std::cerr << "GxsChannelFilesStatusWidget(): can't open file " << fileInfo.path << std::endl; + } + }else{ + QMessageBox::information(this, tr("Play File"), + tr("File %1 does not exist at location.").arg(fileInfo.path.c_str())); + return; + } +} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h index 3d58cc4d7..1effe0549 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h @@ -44,6 +44,7 @@ private slots: void pause(); void resume(); void openFolder(); + void openFile(); private: void setSize(uint64_t size); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui index 39e32271d..c1b02ab83 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui @@ -48,7 +48,7 @@ 2
- + 0 @@ -126,18 +126,31 @@ - + 0 0 + + Play + + + + + + + + 0 + 0 + + Qt::NoFocus - - Open folder + + QToolButton::InstantPopup @@ -148,6 +161,7 @@
+ diff --git a/retroshare-gui/src/gui/icons.qrc b/retroshare-gui/src/gui/icons.qrc index 6e4bd7569..656cdc7fd 100644 --- a/retroshare-gui/src/gui/icons.qrc +++ b/retroshare-gui/src/gui/icons.qrc @@ -60,6 +60,7 @@ icons/png/anonymous.png icons/png/attach-image.png icons/png/attach.png + icons/png/arrow.png icons/png/cert.png icons/png/channels-notify.png icons/png/channels.png diff --git a/retroshare-gui/src/gui/icons/png/arrow.png b/retroshare-gui/src/gui/icons/png/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e238538026d1e88b3f373287ead27e503f5c161d GIT binary patch literal 935 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&v7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xB0oAoIF#H0kf5E^|YQVtoDuIE)Y6b&?c)^@qfi^&iMFBn`u0Wb$)qm#M*O+Ht zWtx2zNHR41XP9}3Y4$aySyx$Sg2Y+pTxFPc3CIA+&OgsG`zjDYxwEb_%(%uf=L*ZL z%WN~Rv(387F!Kyls_!v~G3N@y?8{J*xi^^SfHX4AxxzH(D$sneI9PVpHHMjYftE7Q zx&}0y3CLY?8z>8O-HvEab0B3>666=mz#P8n@?9p4ix-UE8wx&tB+%9V{DqpTwUw?m z*S9a6E0=5j{GpzdSXf!~FX&Yuf5Fb{`FuN0{n!N5&zR)x?!wT)D(eB{a29w(7BevL z9RXp+soH$fKtc8rPhVH|Cv02-W^(6FzMKZs)8Xmj7*cWT?bYxgCP$IhhhhRDXDrnX z8dfYQ|DXN2QSwm>pLs~+^PC62<*h8*6D8W9=thZjZ(4S6?%K4)d*76A^QgQ%*R_86 z>bJtB<uG!l<>snA z+9VXecEOc!trZ_axwL9qo226REVw4F_u+#p_nw-AO#bm}4%`;k;`rdo)l}2i7) z!i{vjjt`OCi)xNFdCXs{a4TKQ<3l9Zq?)!S7yF$GH~n=IK1}9{_|w9qX1}Z9Y`;#z zm&IHWe><4c>~}QW<<}AT(9GrXr-4cF{%(ge-*p7O?B#N)?Pp5dzs=#!dd+|j+T0<3 zx|me=Z*e%QuNm-#n>*xh6H{vaT8F#wng$={O7j%gS#0<)_qT-a-;2w8-`~0Zy~0TH z|DULz@UWElX8(=-M$jeR3Y{f~fzhN|;u=wsl30>zm0Xkxq!^40j0|)Q%ybP6LJZBU zOiiqe4YUmmtPBk9+E2cOq9HdwB{QuOw}v%I+Ic_?k{}y`^V3So6N^$A%FE03GV`*F clM@S4_413-XTP(N0xDwgboFyt=akR{0M4y>Jpcdz literal 0 HcmV?d00001 From e92c89bdfbee8ace9435f2515f16df4c048cac3d Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 11 Jun 2020 20:09:24 +0200 Subject: [PATCH 50/89] forget this to commit --- retroshare-gui/src/gui/qss/stylesheet/Standard.qss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss index 53ac80a44..505fc83c9 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss @@ -791,6 +791,14 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-button { } +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator { + image: none; +} + +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] { + padding-right: 0px; +} + GxsGroupDialog QLabel#groupLogo{ border: 2px solid #CCCCCC; border-radius: 3px; From 2faaccbb7a4963f6d8288282b50c7e99914c02f9 Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 11 Jun 2020 21:55:52 +0200 Subject: [PATCH 51/89] fixed a few cosmetic details in channels model and UI --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 8 +-- .../GxsChannelPostsWidgetWithModel.cpp | 71 +++++++++++++++---- .../GxsChannelPostsWidgetWithModel.h | 3 + .../GxsChannelPostsWidgetWithModel.ui | 16 ++++- 4 files changed, 80 insertions(+), 18 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 94d6f6406..caca20f93 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -145,7 +145,7 @@ int RsGxsChannelPostsModel::rowCount(const QModelIndex& parent) const int RsGxsChannelPostsModel::columnCount(const QModelIndex &/*parent*/) const { - return mColumns ; + return std::min((int)mFilteredPosts.size(),(int)mColumns) ; } bool RsGxsChannelPostsModel::getPostData(const QModelIndex& i,RsGxsChannelPost& fmpe) const @@ -635,7 +635,7 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) RsGxsMessageId postId = mid; - for(uint32_t i=1;i -#define CHAN_DEFAULT_IMAGE ":/icons/png/channels.png" +#define CHAN_DEFAULT_IMAGE ":images/thumb-default-video.png" #define ROLE_PUBLISH FEED_TREEWIDGET_SORTROLE @@ -102,10 +103,17 @@ public: // now fill the data - QPixmap thumbnail; - GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); - - lb->setPixmap(thumbnail); + if(post.mThumbnail.mSize > 0) + { + QPixmap thumbnail; + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); + lb->setPixmap(thumbnail); + } + else + { + QPixmap thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); + lb->setPixmap(thumbnail); + } QFontMetricsF fm(font()); int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ; @@ -114,6 +122,15 @@ public: lb->setFixedSize(W,H); lt->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); + + QFont font = lt->font(); + + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + { + font.setBold(true); + lt->setFont(font); + } + lt->setMaximumWidth(W); lt->setWordWrap(true); @@ -401,15 +418,10 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) updateDisplay(true); break; -// case RsChannelEventCode::READ_STATUS_CHANGED: -// if (FeedItem *feedItem = ui->feedWidget->findFeedItem(GxsChannelPostItem::computeIdentifier(e->mChannelMsgId))) -// if (GxsChannelPostItem *channelPostItem = dynamic_cast(feedItem)) -// channelPostItem->setReadStatus(false,!channelPostItem->isUnread()); -// //channelPostItem->setReadStatus(false,e->Don't get read status. Will be more easier and accurate); -// break; default: break; } @@ -419,18 +431,34 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() { QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + if(!index.isValid() && !mSelectedPost.isNull() && mGroup.mMeta.mGroupId == mSelectedGroup) + { + index = mChannelPostsModel->getIndexOfMessage(mSelectedPost); + whileBlocking(ui->postsTree)->setCurrentIndex(index); + } + if(!index.isValid()) { ui->postDetails_TE->clear(); - ui->postLogo_LB->clear(); + ui->postLogo_LB->hide(); + ui->postName_LB->hide(); mChannelPostFilesModel->clear(); + mSelectedGroup.clear(); + mSelectedPost.clear(); return; } + + ui->postLogo_LB->show(); + ui->postName_LB->show(); + if(index.row()==0 && index.column()==0) std::cerr << "here" << std::endl; RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + mSelectedGroup = mGroup.mMeta.mGroupId; + mSelectedPost = post.mMeta.mMsgId; + mChannelPostFilesModel->setFiles(post.mFiles); auto all_msgs_versions(post.mOlderVersions); @@ -447,7 +475,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() if (post.mThumbnail.mData != NULL) GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL); else - postImage = QPixmap(CHAN_DEFAULT_IMAGE); + postImage = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); int W = QFontMetricsF(font()).height() * 8; @@ -463,6 +491,16 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelPostFiles_TV->setAutoSelect(true); + // Now also set the post as read + + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + { + RsGxsGrpMsgIdPair postId; + postId.second = post.mMeta.mMsgId; + postId.first = post.mMeta.mGroupId; + + RsThread::async([postId]() { rsGxsChannels->markRead(postId, true) ; } ); + } } void GxsChannelPostsWidgetWithModel::updateChannelFiles() @@ -645,6 +683,8 @@ void GxsChannelPostsWidgetWithModel::createMsg() void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGroup &group) { + // save selection if needed + /* IMAGE */ QPixmap chanImage; if (group.mImage.mData != NULL) { @@ -652,6 +692,11 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou } else { chanImage = QPixmap(CHAN_DEFAULT_IMAGE); } + if(group.mMeta.mGroupName.empty()) + ui->channelName_LB->setText(tr("[No name]")); + else + ui->channelName_LB->setText(QString::fromUtf8(group.mMeta.mGroupName.c_str())); + ui->logoLabel->setPixmap(chanImage); ui->logoLabel->setFixedSize(QSize(ui->logoLabel->height()*chanImage.width()/(float)chanImage.height(),ui->logoLabel->height())); // make the logo have the same aspect ratio than the original image diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 90c1f76ad..49e5dc21e 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -166,6 +166,9 @@ private: RsGxsChannelPostFilesModel *mChannelPostFilesModel; RsGxsChannelPostFilesModel *mChannelFilesModel; + RsGxsMessageId mSelectedPost; + RsGxsGroupId mSelectedGroup; + /* UI - from Designer */ Ui::GxsChannelPostsWidgetWithModel *ui; }; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index cfa36e40d..f44372c3a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -161,13 +161,27 @@ - 2 + 0 Channel details + + + + + 75 + true + false + + + + Channel title + + + From 67e8b87750210ddca6428ae18b9057bdff7870de Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 11 Jun 2020 22:17:33 +0200 Subject: [PATCH 52/89] attempt to fix UI bug that automatically deselected after setting msg as read --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 4 +- .../gui/gxschannels/GxsChannelPostsModel.h | 2 +- .../GxsChannelPostsWidgetWithModel.cpp | 48 ++++++++++--------- .../GxsChannelPostsWidgetWithModel.h | 2 +- 4 files changed, 29 insertions(+), 27 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index caca20f93..f22e7ca58 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -398,7 +398,7 @@ void RsGxsChannelPostsModel::clear() initEmptyHierarchy(); postMods(); - emit channelLoaded(); + emit channelPostsLoaded(); } void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector& posts) @@ -431,7 +431,7 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto postMods(); - emit channelLoaded(); + emit channelPostsLoaded(); } void RsGxsChannelPostsModel::update_posts(const RsGxsGroupId& group_id) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h index b82f91783..59d937b6c 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.h @@ -173,7 +173,7 @@ public: void debug_dump(); signals: - void channelLoaded(); // emitted after the posts have been set. Can be used to updated the UI. + void channelPostsLoaded(); // emitted after the posts have been loaded. private: RsGxsChannelGroup mChannelGroup; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 17a9d5d85..31bd27b3f 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -323,7 +323,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->channelFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumnFiles(int,Qt::SortOrder))); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); - connect(mChannelPostsModel,SIGNAL(channelLoaded()),this,SLOT(updateChannelFiles())); + connect(mChannelPostsModel,SIGNAL(channelPostsLoaded()),this,SLOT(postChannelLoad())); QFontMetricsF fm(font()); @@ -431,12 +431,6 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() { QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); - if(!index.isValid() && !mSelectedPost.isNull() && mGroup.mMeta.mGroupId == mSelectedGroup) - { - index = mChannelPostsModel->getIndexOfMessage(mSelectedPost); - whileBlocking(ui->postsTree)->setCurrentIndex(index); - } - if(!index.isValid()) { ui->postDetails_TE->clear(); @@ -503,20 +497,6 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() } } -void GxsChannelPostsWidgetWithModel::updateChannelFiles() -{ - std::list files; - - mChannelPostsModel->getFilesList(files); - mChannelFilesModel->setFiles(files); - - ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); - ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); - ui->channelFiles_TV->setAutoSelect(true); - - //mChannelPostFilesProxyModel->sort(0, Qt::AscendingOrder); -} - void GxsChannelPostsWidgetWithModel::updateGroupData() { if(groupId().isNull()) @@ -548,6 +528,29 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() }); } +void GxsChannelPostsWidgetWithModel::postChannelLoad() +{ + std::cerr << "Post channel load..." << std::endl; + + if(!mSelectedPost.isNull() && mGroup.mMeta.mGroupId == mSelectedGroup) + { + QModelIndex index = mChannelPostsModel->getIndexOfMessage(mSelectedPost); + std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post " << mSelectedPost << std::endl; + whileBlocking(ui->postsTree)->setCurrentIndex(index); + } + + std::list files; + + mChannelPostsModel->getFilesList(files); + mChannelFilesModel->setFiles(files); + + ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); + ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); + ui->channelFiles_TV->setAutoSelect(true); + + +} + void GxsChannelPostsWidgetWithModel::updateDisplay(bool complete) { #ifdef DEBUG_CHANNEL @@ -779,7 +782,6 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou } ui->infoDistribution->setText(distrib_string); - #ifdef TODO ui->infoWidget->show(); ui->feedWidget->hide(); @@ -788,8 +790,8 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou //ui->feedToolButton->setEnabled(false); //ui->fileToolButton->setEnabled(false); #endif - ui->subscribeToolButton->setText(tr("Subscribe ") + " " + QString::number(group.mMeta.mPop) ); + } #ifdef TODO diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 49e5dc21e..7d2b9038c 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -138,7 +138,7 @@ private slots: void setViewMode(int viewMode); void settingsChanged(); void handlePostsTreeSizeChange(QSize s); - void updateChannelFiles(); + void postChannelLoad(); public slots: void sortColumnFiles(int col,Qt::SortOrder so); From 2b8d65b31ffca4494643c66dce7440810e8096b6 Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 11 Jun 2020 22:48:21 +0200 Subject: [PATCH 53/89] Fixing default stylesheet for Channel label --- .../gui/gxschannels/GxsChannelPostsWidgetWithModel.ui | 9 +++++++-- retroshare-gui/src/gui/qss/stylesheet/qss.default | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index f44372c3a..761f65e22 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -169,7 +169,7 @@ - + 75 @@ -337,7 +337,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;">Description</span></p></body></html> @@ -540,6 +540,11 @@ p, li { white-space: pre-wrap; } QToolButton
gui/common/SubscribeToolButton.h
+ + StyledElidedLabel + QLabel +
gui/common/StyledElidedLabel.h
+
RSTreeView QTreeView diff --git a/retroshare-gui/src/gui/qss/stylesheet/qss.default b/retroshare-gui/src/gui/qss/stylesheet/qss.default index f71a4b435..b4e20120a 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/qss.default +++ b/retroshare-gui/src/gui/qss/stylesheet/qss.default @@ -84,7 +84,7 @@ ConfCertDialog QLabel#servicePermissionsLabel qproperty-fontSizeFactor: 125; } -GxsChannelPostsWidget QLabel#nameLabel +GxsChannelPostsWidgetWithModel QLabel#channelName_LB { qproperty-fontSizeFactor: 250; } From f40d7a75b36f009f8d42af2984cb78aa64c5de4e Mon Sep 17 00:00:00 2001 From: csoler Date: Fri, 12 Jun 2020 11:00:51 +0200 Subject: [PATCH 54/89] fixed a few UI bugs in channel posts --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 7 +++++ .../GxsChannelPostsWidgetWithModel.cpp | 27 ++++++++++++------- .../GxsChannelPostsWidgetWithModel.h | 2 +- .../GxsChannelPostsWidgetWithModel.ui | 11 ++++++-- 4 files changed, 35 insertions(+), 12 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index f22e7ca58..ae2c3d119 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -401,6 +401,11 @@ void RsGxsChannelPostsModel::clear() emit channelPostsLoaded(); } +bool operator<(const RsGxsChannelPost& p1,const RsGxsChannelPost& p2) +{ + return p1.mMeta.mPublishTs > p2.mMeta.mPublishTs; +} + void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vector& posts) { preMods(); @@ -413,6 +418,8 @@ void RsGxsChannelPostsModel::setPosts(const RsGxsChannelGroup& group, std::vecto createPostsArray(posts); + std::sort(mPosts.begin(),mPosts.end()); + mFilteredPosts.clear(); for(int i=0;isetPixmap(thumbnail); } - else + else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row) { QPixmap thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); lb->setPixmap(thumbnail); @@ -157,7 +157,7 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & QPixmap pixmap(w.size()); - if(option.state & QStyle::State_Selected) + if((option.state & QStyle::State_Selected) && post.mMeta.mPublishTs > 0) // check if post is selected and is not empty (end of last row) pixmap.fill(QRgb(0xff308dc7)); // I dont know how to grab the backgroud color for selected objects automatically. else pixmap.fill(QRgb(0x00ffffff)); // choose a fully transparent background @@ -323,7 +323,13 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->channelFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumnFiles(int,Qt::SortOrder))); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); - connect(mChannelPostsModel,SIGNAL(channelPostsLoaded()),this,SLOT(postChannelLoad())); + connect(mChannelPostsModel,SIGNAL(channelPostsLoaded()),this,SLOT(postChannelPostLoad())); + + ui->postName_LB->hide(); + ui->postTime_LB->hide(); + ui->postLogo_LB->hide(); + + ui->postDetails_TE->setPlaceholderText(tr("No post selected")); QFontMetricsF fm(font()); @@ -430,12 +436,14 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrpostsTree->selectionModel()->currentIndex(); + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; - if(!index.isValid()) + if(post.mMeta.mPublishTs == 0) { ui->postDetails_TE->clear(); ui->postLogo_LB->hide(); ui->postName_LB->hide(); + ui->postTime_LB->hide(); mChannelPostFilesModel->clear(); mSelectedGroup.clear(); mSelectedPost.clear(); @@ -444,12 +452,11 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->postLogo_LB->show(); ui->postName_LB->show(); + ui->postTime_LB->show(); if(index.row()==0 && index.column()==0) std::cerr << "here" << std::endl; - RsGxsChannelPost post = index.data(Qt::UserRole).value() ; - mSelectedGroup = mGroup.mMeta.mGroupId; mSelectedPost = post.mMeta.mMsgId; @@ -476,10 +483,12 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() // Using fixed width so that the post will not displace the text when we browse. ui->postLogo_LB->setPixmap(postImage); - ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); - ui->postLogo_LB->setFixedSize(W,postImage.height()/(float)postImage.width()*W); + + ui->postName_LB->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); ui->postName_LB->setFixedWidth(W); + ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm")); + ui->postTime_LB->setFixedWidth(W); ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); @@ -528,7 +537,7 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() }); } -void GxsChannelPostsWidgetWithModel::postChannelLoad() +void GxsChannelPostsWidgetWithModel::postChannelPostLoad() { std::cerr << "Post channel load..." << std::endl; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 7d2b9038c..5f2f4adae 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -138,7 +138,7 @@ private slots: void setViewMode(int viewMode); void settingsChanged(); void handlePostsTreeSizeChange(QSize s); - void postChannelLoad(); + void postChannelPostLoad(); public slots: void sortColumnFiles(int col,Qt::SortOrder so); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index f44372c3a..71412862e 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -161,7 +161,7 @@ - 0 + 1 @@ -399,7 +399,7 @@ p, li { white-space: pre-wrap; }
- 2 + 0 @@ -434,6 +434,13 @@ p, li { white-space: pre-wrap; }
+ + + + TextLabel + + + From f88527dbd78fa8b37eb34d317f0f5f5d8c00e778 Mon Sep 17 00:00:00 2001 From: sehraf Date: Fri, 12 Jun 2020 17:28:08 +0200 Subject: [PATCH 55/89] i2pbob: remove sockaddr_storage_tostring --- libretroshare/src/services/autoproxy/p3i2pbob.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index 7fd7594b9..a170d08ca 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -936,7 +936,7 @@ void p3I2pBob::finalizeSettings_locked() sockaddr_storage_setipv4(mI2PProxyAddr, (sockaddr_in*)&proxy); sockaddr_storage_setport(mI2PProxyAddr, 2827); - RS_DBG4("using ", sockaddr_storage_tostring(mI2PProxyAddr)); + RS_DBG4("using ", mI2PProxyAddr); RS_DBG4("using ", mSetting.address.base32); peerState ps; From 5ff5a32df70c001caf930a3cdfabea3f8bf0c24d Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Fri, 12 Jun 2020 18:41:42 +0200 Subject: [PATCH 56/89] Proper naming for RsRandom string functions --- libretroshare/src/util/rsrandom.cc | 25 +++++++++++++++++++------ libretroshare/src/util/rsrandom.h | 12 +++++++++++- 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/libretroshare/src/util/rsrandom.cc b/libretroshare/src/util/rsrandom.cc index 7ea8eb968..dc7d9ac67 100644 --- a/libretroshare/src/util/rsrandom.cc +++ b/libretroshare/src/util/rsrandom.cc @@ -121,13 +121,26 @@ double RsRandom::random_f64() return random_u64() / (double)(~(uint64_t)0) ; } -std::string RsRandom::random_alphaNumericString(uint32_t len) +/*static*/ std::string RsRandom::alphaNumeric(uint32_t length) { - std::string s = "" ; + std::string s; + while(s.size() < length) + { + uint8_t rChar; random_bytes(&rChar, 1); rChar = rChar % 123; + /* if(isalnum(val)) isalnum result may vary depend on locale!! */ + if( (rChar >= 48 && rChar <= 57) /* 0-9 */ || + (rChar >= 65 && rChar <= 90) /* A-Z */ || + (rChar >= 97 && rChar <= 122) /* a-z */ ) + s += static_cast(rChar); + } - for(uint32_t i=0;i(&ret[0]), length); + for(uint32_t i=0; i Date: Sat, 13 Jun 2020 10:57:08 +0200 Subject: [PATCH 57/89] fix typo --- libretroshare/src/services/autoproxy/rsautoproxymonitor.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc index 3c52cfdc5..d58c871e3 100644 --- a/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc +++ b/libretroshare/src/services/autoproxy/rsautoproxymonitor.cc @@ -265,7 +265,7 @@ void rsAutoProxyMonitor::taskDone(taskTicket *t, autoProxyStatus::autoProxyStatu // we must take care of deleting cleanUp = true; if(t->data) - RS_ERR("sync call with data attached but no callback set!"); + RS_ERR("async call with data attached but no callback set!"); } if (cleanUp) { From 8323b94a6a13e574e95f909f3b6670568b85d945 Mon Sep 17 00:00:00 2001 From: sehraf Date: Sat, 13 Jun 2020 10:58:44 +0200 Subject: [PATCH 58/89] use modern c++ --- libretroshare/src/util/i2pcommon.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index 575b0fec0..eef359576 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -39,10 +39,7 @@ std::string keyToBase32Addr(const std::string &key) } const std::string makeOption(const std::string &lhs, const int8_t &rhs) { - // convert number to int - std::ostringstream oss; - oss << (int)rhs; - return lhs + "=" + oss.str(); + return lhs + "=" + std::to_string(rhs); } uint16_t readTwoBytesBE(std::vector::const_iterator &p) From f18d49db483b0dc9d8f8371b538dd8f7d506a419 Mon Sep 17 00:00:00 2001 From: sehraf Date: Sat, 13 Jun 2020 11:07:07 +0200 Subject: [PATCH 59/89] use RsRandom::alphaNumeric --- libretroshare/src/services/autoproxy/p3i2pbob.cc | 10 +++------- libretroshare/src/util/i2pcommon.cpp | 8 -------- libretroshare/src/util/i2pcommon.h | 13 ------------- 3 files changed, 3 insertions(+), 28 deletions(-) diff --git a/libretroshare/src/services/autoproxy/p3i2pbob.cc b/libretroshare/src/services/autoproxy/p3i2pbob.cc index a170d08ca..f9eb3d9b3 100644 --- a/libretroshare/src/services/autoproxy/p3i2pbob.cc +++ b/libretroshare/src/services/autoproxy/p3i2pbob.cc @@ -945,13 +945,9 @@ void p3I2pBob::finalizeSettings_locked() // setup commands // new lines are appended later! - // generate random suffix for name - // RSRandom::random_alphaNumericString can return very weird looking strings like: ,,@z+M - // use base32 instead - size_t len = 5; // 5 characters = 8 base32 symbols - std::vector tmp(len); - RSRandom::random_bytes(tmp.data(), len); - const std::string location = Radix32::encode(tmp.data(), len); + // generate 8 characater long random suffix for name + constexpr size_t len = 8; + const std::string location = RsRandom::alphaNumeric(len); RS_DBG4("using suffix ", location); mTunnelName = "RetroShare-" + location; diff --git a/libretroshare/src/util/i2pcommon.cpp b/libretroshare/src/util/i2pcommon.cpp index eef359576..ec2ebfd6b 100644 --- a/libretroshare/src/util/i2pcommon.cpp +++ b/libretroshare/src/util/i2pcommon.cpp @@ -5,14 +5,6 @@ namespace i2p { -const std::string generateNameSuffix(const size_t len) { - std::vector tmp(len); - RsRandom::random_bytes(tmp.data(), len); - const std::string location = Radix32::encode(tmp.data(), len); - - return location; -} - std::string keyToBase32Addr(const std::string &key) { std::string copy(key); diff --git a/libretroshare/src/util/i2pcommon.h b/libretroshare/src/util/i2pcommon.h index 63f1ba5af..01d655bf9 100644 --- a/libretroshare/src/util/i2pcommon.h +++ b/libretroshare/src/util/i2pcommon.h @@ -184,19 +184,6 @@ static const std::array, 12> signingKeyLengths { /*SigningKeyType::RedDSA_SHA512_Ed25519 */ std::make_pair( 32, 32), }; - -/** - * @brief generateNameSuffix Generates a base32 name suffix for tunnel identification - * @param len lenght of random bytes, will be expanded by base32 encoding - * @return base32 string - * - * RSRandom::random_alphaNumericString can return very weird looking strings like: ,,@z+M - * -> so use base32 instead - * - * 5 characters = 8 base32 symbols - */ -const std::string generateNameSuffix(const size_t len = 5); - /** * @brief makeOption Creates the string "lhs=rhs" used by BOB and SAM. Converts rhs * @param lhs option to set From ebaf5e63c3f4135a847a85d1aa607a4c19144f2f Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 13 Jun 2020 13:38:19 +0200 Subject: [PATCH 60/89] added star for new posts --- .../GxsChannelPostsWidgetWithModel.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 49d0ace88..5f3fb58de 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -77,6 +77,7 @@ static const int CHANNEL_TABS_POSTS = 1; // This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen. #define THUMBNAIL_OVERSAMPLE_FACTOR 2.0 +#define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png" Q_DECLARE_METATYPE(RsGxsFile) @@ -103,23 +104,19 @@ public: // now fill the data + QPixmap thumbnail; + if(post.mThumbnail.mSize > 0) - { - QPixmap thumbnail; GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); - lb->setPixmap(thumbnail); - } else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row) - { - QPixmap thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); - lb->setPixmap(thumbnail); - } + thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); QFontMetricsF fm(font()); int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ; int H = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_H * fm.height() ; lb->setFixedSize(W,H); + lb->setPixmap(thumbnail); lt->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); @@ -164,6 +161,13 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) + { + QPainter p(&pixmap); + QFontMetricsF fm(option.font); + p.drawPixmap(QPoint(6.2*fm.height(),6.9*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(7*fm.height(),7*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation)); + } + // debug // if(index.row()==0 && index.column()==0) // { From f8deebfc5a85237e4c17695856bacb81a216c7a0 Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 13 Jun 2020 14:31:39 +0200 Subject: [PATCH 61/89] fixed updating of data after read status change --- .../gui/gxschannels/GxsChannelPostsModel.cpp | 71 ++++++++++++++++++- .../gui/gxschannels/GxsChannelPostsModel.h | 5 +- .../GxsChannelPostsWidgetWithModel.cpp | 5 +- 3 files changed, 77 insertions(+), 4 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index ae2c3d119..9c40f97e3 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -43,7 +43,76 @@ std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsew RsGxsChannelPostsModel::RsGxsChannelPostsModel(QObject *parent) : QAbstractItemModel(parent), mTreeMode(TREE_MODE_PLAIN), mColumns(6) { - initEmptyHierarchy(); + initEmptyHierarchy(); + + mEventHandlerId = 0; + // Needs to be asynced because this function is called by another thread! + + rsEvents->registerEventsHandler( [this](std::shared_ptr event) + { + RsQThreadUtils::postToObject([=](){ handleEvent_main_thread(event); }, this ); + }, mEventHandlerId, RsEventType::GXS_CHANNELS ); +} + +RsGxsChannelPostsModel::~RsGxsChannelPostsModel() +{ + rsEvents->unregisterEventsHandler(mEventHandlerId); +} + +void RsGxsChannelPostsModel::handleEvent_main_thread(std::shared_ptr event) +{ + const RsGxsChannelEvent *e = dynamic_cast(event.get()); + + if(!e) + return; + + switch(e->mChannelEventCode) + { + case RsChannelEventCode::UPDATED_MESSAGE: + case RsChannelEventCode::READ_STATUS_CHANGED: + { + // Normally we should just emit dataChanged() on the index of the data that has changed: + // + // We need to update the data! + + if(e->mChannelGroupId == mChannelGroup.mMeta.mGroupId) + RsThread::async([this, e]() + { + // 1 - get message data from p3GxsChannels + + std::vector posts; + std::vector comments; + std::vector votes; + + if(!rsGxsChannels->getChannelContent(mChannelGroup.mMeta.mGroupId,std::set{ e->mChannelMsgId }, posts,comments,votes)) + { + std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve channel message data for channel/msg " << e->mChannelGroupId << "/" << e->mChannelMsgId << std::endl; + return; + } + + // 2 - update the model in the UI thread. + + RsQThreadUtils::postToObject( [posts,comments,votes,this]() + { + for(uint32_t i=0;i #include @@ -62,7 +63,7 @@ class RsGxsChannelPostsModel : public QAbstractItemModel public: explicit RsGxsChannelPostsModel(QObject *parent = NULL); - ~RsGxsChannelPostsModel(){} + virtual ~RsGxsChannelPostsModel() override; static const int COLUMN_THREAD_NB_COLUMNS = 0x01; @@ -217,6 +218,7 @@ private: void createPostsArray(std::vector &posts); void setPosts(const RsGxsChannelGroup& group, std::vector &posts); void initEmptyHierarchy(); + void handleEvent_main_thread(std::shared_ptr event); std::vector mFilteredPosts; // stores the list of displayes indices due to filtering. std::vector mPosts ; // store the list of posts updated from rsForums. @@ -229,5 +231,6 @@ private: QColor mTextColorNotSubscribed ; QColor mTextColorMissing ; + RsEventsHandlerId_t mEventHandlerId ; friend class const_iterator; }; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 5f3fb58de..6faf2d122 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -428,10 +428,11 @@ void GxsChannelPostsWidgetWithModel::handleEvent_main_thread(std::shared_ptrmChannelGroupId == groupId()) updateDisplay(true); - break; + } + default: break; } From f13b429f1c09adff8bd99439e1c4a26766d305f8 Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Sat, 13 Jun 2020 14:58:30 +0200 Subject: [PATCH 62/89] Document what chars are return RsRandom::alphaNumeric As requested by Cyril --- libretroshare/src/util/rsrandom.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libretroshare/src/util/rsrandom.h b/libretroshare/src/util/rsrandom.h index 5e7e0d92c..94d619537 100644 --- a/libretroshare/src/util/rsrandom.h +++ b/libretroshare/src/util/rsrandom.h @@ -48,7 +48,7 @@ public: static void random_bytes(uint8_t* data, uint32_t length); - /** Return a random alphanumeric string of the given lenght */ + /// Return a random alphanumeric *[0-9,A-Z,a-z] string of the given lenght static std::string alphaNumeric(uint32_t length); /** Return a random printable string of the given lenght */ From f1092b4a292c3b573c389d757061c657b98b563b Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 13 Jun 2020 15:00:30 +0200 Subject: [PATCH 63/89] added post editing --- .../GxsChannelPostsWidgetWithModel.cpp | 64 +++++++------------ .../GxsChannelPostsWidgetWithModel.h | 2 + .../GxsChannelPostsWidgetWithModel.ui | 3 + 3 files changed, 28 insertions(+), 41 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 6faf2d122..477c4f850 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -19,6 +19,7 @@ *******************************************************************************/ #include +#include #include #include @@ -261,40 +262,6 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con } } -// class RsGxsChannelPostFilesProxyModel: public QSortFilterProxyModel -// { -// public: -// RsGxsChannelPostFilesProxyModel(QObject *parent = NULL): QSortFilterProxyModel(parent) {} -// -// bool lessThan(const QModelIndex& left, const QModelIndex& right) const override -// { -// return left.data(RsGxsChannelPostFilesModel::SortRole) < right.data(RsGxsChannelPostFilesModel::SortRole) ; -// } -// -// bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override -// { -// if(filter_list.empty()) -// return true; -// -// QString name = sourceModel()->data(sourceModel()->index(source_row,RsGxsChannelPostFilesModel::COLUMN_FILES_NAME,source_parent)).toString(); -// -// for(auto& s:filter_list) -// if(!name.contains(s,Qt::CaseInsensitive)) -// return false; -// -// return true; -// } -// -// void setFilterList(const QStringList& str) -// { -// filter_list = str; -// invalidateFilter(); -// } -// -// private: -// QStringList filter_list; -// }; - /** Constructor */ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupId &channelId, QWidget *parent) : GxsMessageFrameWidget(rsGxsChannels, parent), @@ -312,11 +279,6 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->channelPostFiles_TV->setSortingEnabled(true); ui->channelPostFiles_TV->sortByColumn(0, Qt::AscendingOrder); -// mChannelPostFilesProxyModel = new RsGxsChannelPostFilesProxyModel(this); -// mChannelPostFilesProxyModel->setFilterCaseSensitivity(Qt::CaseInsensitive); -// mChannelPostFilesProxyModel->setSourceModel(mChannelPostFilesModel); -// mChannelPostFilesProxyModel->setDynamicSortFilter(true); - ui->channelFiles_TV->setModel(mChannelFilesModel = new RsGxsChannelPostFilesModel()); ui->channelFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); ui->channelFiles_TV->setPlaceholderText(tr("No files in the channel, or no channel selected")); @@ -327,6 +289,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->channelFiles_TV->header(),SIGNAL(sortIndicatorChanged(int,Qt::SortOrder)), this, SLOT(sortColumnFiles(int,Qt::SortOrder))); connect(ui->postsTree->selectionModel(),SIGNAL(selectionChanged(const QItemSelection&,const QItemSelection&)),this,SLOT(showPostDetails())); + connect(ui->postsTree,SIGNAL(customContextMenuRequested(const QPoint&)),this,SLOT(postContextMenu(const QPoint&))); + connect(mChannelPostsModel,SIGNAL(channelPostsLoaded()),this,SLOT(postChannelPostLoad())); ui->postName_LB->hide(); @@ -404,10 +368,28 @@ void GxsChannelPostsWidgetWithModel::sortColumnFiles(int col,Qt::SortOrder so) mChannelFilesModel->sort(col,so); } +void GxsChannelPostsWidgetWithModel::postContextMenu(const QPoint&) +{ + QMenu menu(this); + + if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) + { + menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost())); + menu.exec(QCursor::pos()); + } +} + +void GxsChannelPostsWidgetWithModel::editPost() +{ + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + CreateGxsChannelMsg *msgDialog = new CreateGxsChannelMsg(post.mMeta.mGroupId,post.mMeta.mMsgId); + msgDialog->show(); +} + void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s) { -// adjustSize(); -// int n_columns = std::max(1,(int)floor(s.width() / (COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()))); std::cerr << "nb columns: " << n_columns << std::endl; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 5f2f4adae..f278fcf89 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -139,6 +139,8 @@ private slots: void settingsChanged(); void handlePostsTreeSizeChange(QSize s); void postChannelPostLoad(); + void editPost(); + void postContextMenu(const QPoint&); public slots: void sortColumnFiles(int col,Qt::SortOrder so); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 71412862e..20b8e27c4 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -364,6 +364,9 @@ p, li { white-space: pre-wrap; } Qt::Vertical + + Qt::CustomContextMenu + QAbstractScrollArea::AdjustToContents From fd85965e7d6610b71d7f09d04cba941fe364d639 Mon Sep 17 00:00:00 2001 From: csoler Date: Sat, 13 Jun 2020 16:14:15 +0200 Subject: [PATCH 64/89] added preview widget when creating pochannel posts --- .../gui/gxschannels/CreateGxsChannelMsg.cpp | 14 +- .../src/gui/gxschannels/CreateGxsChannelMsg.h | 1 + .../gui/gxschannels/CreateGxsChannelMsg.ui | 418 +++++++++--------- .../gui/gxschannels/GxsChannelPostThumbnail.h | 123 ++++++ .../GxsChannelPostsWidgetWithModel.cpp | 71 +-- retroshare-gui/src/retroshare-gui.pro | 1 + 6 files changed, 354 insertions(+), 274 deletions(-) create mode 100644 retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp index 56c910237..ef1a6271e 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.cpp @@ -72,7 +72,8 @@ CreateGxsChannelMsg::CreateGxsChannelMsg(const RsGxsGroupId &cId, RsGxsMessageId connect(addFileButton, SIGNAL(clicked() ), this , SLOT(addExtraFile())); connect(addfilepushButton, SIGNAL(clicked() ), this , SLOT(addExtraFile())); - + connect(subjectEdit,SIGNAL(textChanged(const QString&)),this,SLOT(updatePreviewText(const QString&))); + connect(addThumbnailButton, SIGNAL(clicked() ), this , SLOT(addThumbnail())); connect(thumbNailCb, SIGNAL(toggled(bool)), this, SLOT(allowAutoMediaThumbNail(bool))); connect(stackedWidget, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(contextMenu(QPoint))); @@ -605,6 +606,11 @@ void CreateGxsChannelMsg::saveChannelInfo(const RsGroupMetaData &meta) subjectEdit->setFocus(); } +void CreateGxsChannelMsg::updatePreviewText(const QString& s) +{ + preview_W->setText(s); +} + void CreateGxsChannelMsg::sendMsg() { #ifdef DEBUG_CREATE_GXS_MSG @@ -717,7 +723,7 @@ void CreateGxsChannelMsg::sendMessage(const std::string &subject, const std::str void CreateGxsChannelMsg::addThumbnail() { - QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 156, 107); + QPixmap img = misc::getOpenThumbnailedPicture(this, tr("Load thumbnail picture"), 107,156); // these absolute sizes are terrible if (img.isNull()) return; @@ -725,7 +731,7 @@ void CreateGxsChannelMsg::addThumbnail() picture = img; // to show the selected - thumbnail_label->setPixmap(picture); + preview_W->setPixmap(picture); } void CreateGxsChannelMsg::loadOriginalChannelPostInfo() @@ -769,7 +775,7 @@ void CreateGxsChannelMsg::loadOriginalChannelPostInfo() if(post.mThumbnail.mData != NULL) { GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData,post.mThumbnail.mSize,picture,GxsIdDetails::ORIGINAL); - thumbnail_label->setPixmap(picture); + preview_W->setPixmap(picture); } diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h index 7c1058695..44d867ba2 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.h @@ -58,6 +58,7 @@ private slots: void addExtraFile(); void checkAttachmentReady(); void deleteAttachment(); + void updatePreviewText(const QString &); void cancelMsg(); void sendMsg(); diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui index 703f43bb1..d8baaba24 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui @@ -52,52 +52,39 @@ QFrame::Raised - - - - Channel Post - - - - :/icons/png/comment.png:/icons/png/comment.png - - - - 24 - 24 - - - - - - - - Attachments - - - - :/icons/png/attachements.png:/icons/png/attachements.png - - - - 24 - 24 - - - - - - - - Qt::Horizontal - - - - 486 - 20 - - - + + + + + + Generate mass data + + + + + + + 1 + + + 999 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + @@ -114,128 +101,8 @@ 0 - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - 156 - 107 - - - - - 0 - 0 - - - - :/images/thumb-default-video.png - - - - - - - <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> -<html><head><meta name="qrichtext" content="1" /><style type="text/css"> -p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;">Attachments:</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Use Drag and Drop / Add Files button, to Hash new files.</span></p> -<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Copy/Paste RetroShare links from your shares</span></p></body></html> - - - - - - - Add Channel Thumbnail - - - - :/icons/png/add-image.png:/icons/png/add-image.png - - - - 24 - 24 - - - - - - - - Add File to Attach - - - - :/icons/png/add-file.png:/icons/png/add-file.png - - - - 24 - 24 - - - - - - - - Qt::Horizontal - - - QSizePolicy::Expanding - - - - 40 - 20 - - - - - - - - - - Message - - - - 0 - - - - - Title - - - - - - - - - - + + @@ -268,6 +135,119 @@ p, li { white-space: pre-wrap; } + + + + + + + + + + + <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> +<html><head><meta name="qrichtext" content="1" /><style type="text/css"> +p, li { white-space: pre-wrap; } +</style></head><body style=" font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;"> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-family:'MS Shell Dlg 2'; font-size:10pt; font-weight:600;">Attachments:</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Use Drag and Drop / Add Files button, to Hash new files.</span></p> +<p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><img src=":/images/feedback_arrow.png" /><span style=" font-family:'MS Shell Dlg 2'; font-size:8pt;"> Copy/Paste RetroShare links from your shares</span></p></body></html> + + + + + + + + + Add Channel Thumbnail + + + + :/icons/png/add-image.png:/icons/png/add-image.png + + + + 24 + 24 + + + + + + + + Add File to Attach + + + + :/icons/png/add-file.png:/icons/png/add-file.png + + + + 24 + 24 + + + + + + + + Qt::Horizontal + + + QSizePolicy::Expanding + + + + 40 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + Message + + + + 0 + + + + + Title + + + + + + + + + @@ -329,7 +309,7 @@ p, li { white-space: pre-wrap; } 0 0 - 632 + 635 24 @@ -403,39 +383,65 @@ p, li { white-space: pre-wrap; } - - - - - - Generate mass data - - - - - - - 1 - - - 999 - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - + + + + Qt::Horizontal + + + + 486 + 20 + + + + + + + + Attachments + + + + :/icons/png/attachements.png:/icons/png/attachements.png + + + + 24 + 24 + + + + + + + + Channel Post + + + + :/icons/png/comment.png:/icons/png/comment.png + + + + 24 + 24 + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + @@ -455,10 +461,16 @@ p, li { white-space: pre-wrap; }
util/RichTextEdit.h
1 + + ChannelPostThumbnailView + QWidget +
gui/gxschannels/GxsChannelPostThumbnail.h
+ 1 +
- + diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h new file mode 100644 index 000000000..fd5c82d6b --- /dev/null +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h @@ -0,0 +1,123 @@ +/******************************************************************************* + * retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h * + * * + * Copyright 2020 by Retroshare Team * + * * + * This program is free software: you can redistribute it and/or modify * + * it under the terms of the GNU Affero General Public License as * + * published by the Free Software Foundation, either version 3 of the * + * License, or (at your option) any later version. * + * * + * This program is distributed in the hope that it will be useful, * + * but WITHOUT ANY WARRANTY; without even the implied warranty of * + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * + * GNU Affero General Public License for more details. * + * * + * You should have received a copy of the GNU Affero General Public License * + * along with this program. If not, see . * + * * + *******************************************************************************/ + +#pragma once + +#include + +#include +#include +#include + +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsidentity.h" + +#include "gui/gxs/GxsIdDetails.h" +#include "gui/common/FilesDefs.h" + +// Class to paint the thumbnails with title + +class ChannelPostThumbnailView: public QWidget +{ +public: + // This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen. + static constexpr float THUMBNAIL_OVERSAMPLE_FACTOR = 2.0; + + // Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good. + + static const int THUMBNAIL_W = 4; + static const int THUMBNAIL_H = 6; + + static constexpr char *CHAN_DEFAULT_IMAGE = ":images/thumb-default-video.png"; + + virtual ~ChannelPostThumbnailView() + { + delete lb; + delete lt; + } + + ChannelPostThumbnailView(QWidget *parent=NULL): QWidget(parent) + { + init(FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE), QString("New post"),false); + } + + ChannelPostThumbnailView(const RsGxsChannelPost& post,QWidget *parent=NULL) + : QWidget(parent) + { + // now fill the data + + QPixmap thumbnail; + + if(post.mThumbnail.mSize > 0) + GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); + else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row) + thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); + + init(thumbnail, QString::fromUtf8(post.mMeta.mMsgName.c_str()), IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus) ); + + } + + void init(const QPixmap& thumbnail,const QString& msg,bool is_msg_new) + { + QVBoxLayout *layout = new QVBoxLayout(this); + + lb = new QLabel(this); + lb->setScaledContents(true); + layout->addWidget(lb); + + lt = new QLabel(this); + layout->addWidget(lt); + + setLayout(layout); + + setSizePolicy(QSizePolicy::Maximum,QSizePolicy::Maximum); + + QFontMetricsF fm(font()); + int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ; + int H = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_H * fm.height() ; + + lb->setFixedSize(W,H); + lb->setPixmap(thumbnail); + + lt->setText(msg); + + QFont font = lt->font(); + + if(is_msg_new) + { + font.setBold(true); + lt->setFont(font); + } + + lt->setMaximumWidth(W); + lt->setWordWrap(true); + + adjustSize(); + update(); + } + + void setPixmap(const QPixmap& p) { lb->setPixmap(p); } + void setText(const QString& s) { lt->setText(s); } + +private: + QLabel *lb; + QLabel *lt; +}; + diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 477c4f850..2d64bf8c8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -44,11 +44,10 @@ #include "GxsChannelPostsModel.h" #include "GxsChannelPostFilesModel.h" #include "GxsChannelFilesStatusWidget.h" +#include "GxsChannelPostThumbnail.h" #include -#define CHAN_DEFAULT_IMAGE ":images/thumb-default-video.png" - #define ROLE_PUBLISH FEED_TREEWIDGET_SORTROLE /**** @@ -64,11 +63,6 @@ static const int CHANNEL_TABS_POSTS = 1; #define VIEW_MODE_FEEDS 1 #define VIEW_MODE_FILES 2 -// Size of thumbnails as a function of the height of the font. An aspect ratio of 3/4 is good. - -#define THUMBNAIL_W 4 -#define THUMBNAIL_H 6 - // Determine the Shape and size of cells as a factor of the font height. An aspect ratio of 3/4 is what's needed // for the image, so it's important that the height is a bit larger so as to leave some room for the text. // @@ -76,67 +70,10 @@ static const int CHANNEL_TABS_POSTS = 1; #define COLUMN_SIZE_FONT_FACTOR_W 6 #define COLUMN_SIZE_FONT_FACTOR_H 10 -// This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen. -#define THUMBNAIL_OVERSAMPLE_FACTOR 2.0 #define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png" Q_DECLARE_METATYPE(RsGxsFile) -// Class to paint the thumbnails with title - -class ThumbnailView: public QWidget -{ -public: - ThumbnailView(const RsGxsChannelPost& post,QWidget *parent=NULL) - : QWidget(parent) - { - QVBoxLayout *layout = new QVBoxLayout(this); - - QLabel *lb = new QLabel(this); - lb->setScaledContents(true); - layout->addWidget(lb); - - QLabel *lt = new QLabel(this); - layout->addWidget(lt); - - setLayout(layout); - - setSizePolicy(QSizePolicy::MinimumExpanding,QSizePolicy::MinimumExpanding); - - // now fill the data - - QPixmap thumbnail; - - if(post.mThumbnail.mSize > 0) - GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, thumbnail,GxsIdDetails::ORIGINAL); - else if(post.mMeta.mPublishTs > 0) // this is for testing that the post is not an empty post (happens at the end of the last row) - thumbnail = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); - - QFontMetricsF fm(font()); - int W = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_W * fm.height() ; - int H = THUMBNAIL_OVERSAMPLE_FACTOR * THUMBNAIL_H * fm.height() ; - - lb->setFixedSize(W,H); - lb->setPixmap(thumbnail); - - lt->setText(QString::fromUtf8(post.mMeta.mMsgName.c_str())); - - QFont font = lt->font(); - - if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) - { - font.setBold(true); - lt->setFont(font); - } - - lt->setMaximumWidth(W); - lt->setWordWrap(true); - - adjustSize(); - update(); - } -}; - // Delegate used to paint into the table of thumbnails void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const @@ -151,7 +88,7 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & painter->fillRect( option.rect, option.backgroundBrush); painter->restore(); - ThumbnailView w(post); + ChannelPostThumbnailView w(post); QPixmap pixmap(w.size()); @@ -463,7 +400,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() if (post.mThumbnail.mData != NULL) GxsIdDetails::loadPixmapFromData(post.mThumbnail.mData, post.mThumbnail.mSize, postImage,GxsIdDetails::ORIGINAL); else - postImage = FilesDefs::getPixmapFromQtResourcePath(CHAN_DEFAULT_IMAGE); + postImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); int W = QFontMetricsF(font()).height() * 8; @@ -689,7 +626,7 @@ void GxsChannelPostsWidgetWithModel::insertChannelDetails(const RsGxsChannelGrou if (group.mImage.mData != NULL) { GxsIdDetails::loadPixmapFromData(group.mImage.mData, group.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); } else { - chanImage = QPixmap(CHAN_DEFAULT_IMAGE); + chanImage = QPixmap(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); } if(group.mMeta.mGroupName.empty()) ui->channelName_LB->setText(tr("[No name]")); diff --git a/retroshare-gui/src/retroshare-gui.pro b/retroshare-gui/src/retroshare-gui.pro index 69dfc6b85..ce23a4f9c 100644 --- a/retroshare-gui/src/retroshare-gui.pro +++ b/retroshare-gui/src/retroshare-gui.pro @@ -1337,6 +1337,7 @@ gxschannels { gui/gxschannels/GxsChannelPostsModel.h \ gui/gxschannels/GxsChannelPostFilesModel.h \ gui/gxschannels/GxsChannelFilesWidget.h \ + gui/gxschannels/GxsChannelPostThumbnail.h \ gui/gxschannels/GxsChannelFilesStatusWidget.h \ gui/feeds/GxsChannelGroupItem.h \ gui/feeds/GxsChannelPostItem.h \ From d1d977376d611cd0f2ad0ef5f9909799a8219f5f Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 14 Jun 2020 13:03:12 +0200 Subject: [PATCH 65/89] improved scrolling and explicitly disabled horizontal scroll slider --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 2d64bf8c8..b834e42e2 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -209,6 +209,9 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel()); ui->postsTree->setItemDelegate(new ChannelPostDelegate()); + ui->postsTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // prevents bug on w10, since row size depends on widget width + ui->postsTree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);// more beautiful if we scroll at pixel level + ui->postsTree->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this)); ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); From 0b06515dfe3b50e58b62a873df3c0d5b2d23148a Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 11 Jun 2020 16:32:55 +0200 Subject: [PATCH 66/89] Added open file feature for file status widget * Added open file feature for file status widget --- .../GxsChannelFilesStatusWidget.cpp | 70 +++++++++++++++--- .../gxschannels/GxsChannelFilesStatusWidget.h | 1 + .../GxsChannelFilesStatusWidget.ui | 22 +++++- retroshare-gui/src/gui/icons.qrc | 1 + retroshare-gui/src/gui/icons/png/arrow.png | Bin 0 -> 935 bytes 5 files changed, 81 insertions(+), 13 deletions(-) create mode 100644 retroshare-gui/src/gui/icons/png/arrow.png diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp index c28601fed..bc019e426 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.cpp @@ -18,6 +18,7 @@ * * *******************************************************************************/ +#include #include #include #include @@ -27,6 +28,8 @@ #include "GxsChannelFilesStatusWidget.h" #include "ui_GxsChannelFilesStatusWidget.h" #include "gui/common/RsUrlHandler.h" +#include "gui/common/FilesDefs.h" +#include "util/misc.h" #include "retroshare/rsfiles.h" @@ -40,11 +43,21 @@ GxsChannelFilesStatusWidget::GxsChannelFilesStatusWidget(const RsGxsFile &file, setSize(mFile.mSize); /* Connect signals */ - connect(ui->downloadToolButton, SIGNAL(clicked()), this, SLOT(download())); + connect(ui->downloadPushButton, SIGNAL(clicked()), this, SLOT(download())); connect(ui->resumeToolButton, SIGNAL(clicked()), this, SLOT(resume())); connect(ui->pauseToolButton, SIGNAL(clicked()), this, SLOT(pause())); connect(ui->cancelToolButton, SIGNAL(clicked()), this, SLOT(cancel())); - connect(ui->openFolderToolButton, SIGNAL(clicked()), this, SLOT(openFolder())); + connect(ui->openFilePushButton, SIGNAL(clicked()), this, SLOT(openFile())); + + ui->downloadPushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/download.png")); + ui->openFolderToolButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/arrow.png")); + + QAction *openfolder = new QAction(tr("Open folder"), this); + connect(openfolder, SIGNAL(triggered()), this, SLOT(openFolder())); + + QMenu *menu = new QMenu(); + menu->addAction(openfolder); + ui->openFolderToolButton->setMenu(menu); check(); } @@ -80,6 +93,17 @@ void GxsChannelFilesStatusWidget::check() if (rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) { mState = STATE_LOCAL; setSize(fileInfo.size); + + /* check if the file is a media file */ + if (!misc::isPreviewable(QFileInfo(QString::fromUtf8(fileInfo.path.c_str())).suffix())) + { + /* check if the file is not a media file and change text */ + ui->openFilePushButton->setText(tr("Open file")); + } else { + ui->openFilePushButton->setText(tr("Play")); + ui->openFilePushButton->setIcon(FilesDefs::getIconFromQtResourcePath(":/icons/png/play.png")); + } + } else { FileInfo fileInfo; bool detailsOk = rsFiles->FileDetails(mFile.mHash, RS_FILE_HINTS_DOWNLOAD | RS_FILE_HINTS_SPEC_ONLY, fileInfo); @@ -126,11 +150,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_ERROR: repeat = 0; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Error"); @@ -140,11 +165,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_REMOTE: repeat = 30000; - ui->downloadToolButton->show(); + ui->downloadPushButton->show(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); break; @@ -152,11 +178,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_DOWNLOAD: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->show(); ui->cancelToolButton->show(); ui->progressBar->show(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); break; @@ -164,11 +191,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_PAUSED: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->show(); ui->pauseToolButton->hide(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Paused"); @@ -178,11 +206,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_WAITING: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->show(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Waiting"); @@ -192,11 +221,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_CHECKING: repeat = 1000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->show(); ui->progressBar->hide(); + ui->openFilePushButton->hide(); ui->openFolderToolButton->hide(); statusText = tr("Checking"); @@ -206,11 +236,12 @@ void GxsChannelFilesStatusWidget::check() case STATE_LOCAL: repeat = 60000; - ui->downloadToolButton->hide(); + ui->downloadPushButton->hide(); ui->resumeToolButton->hide(); ui->pauseToolButton->hide(); ui->cancelToolButton->hide(); ui->progressBar->hide(); + ui->openFilePushButton->show(); ui->openFolderToolButton->show(); break; @@ -296,3 +327,24 @@ void GxsChannelFilesStatusWidget::openFolder() } } } + +void GxsChannelFilesStatusWidget::openFile() +{ + FileInfo fileInfo; + if (!rsFiles->alreadyHaveFile(mFile.mHash, fileInfo)) { + return; + } + + /* open file with a suitable application */ + QFileInfo qinfo; + qinfo.setFile(QString::fromUtf8(fileInfo.path.c_str())); + if (qinfo.exists()) { + if (!RsUrlHandler::openUrl(QUrl::fromLocalFile(qinfo.absoluteFilePath()))) { + std::cerr << "GxsChannelFilesStatusWidget(): can't open file " << fileInfo.path << std::endl; + } + }else{ + QMessageBox::information(this, tr("Play File"), + tr("File %1 does not exist at location.").arg(fileInfo.path.c_str())); + return; + } +} diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h index 3d58cc4d7..1effe0549 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.h @@ -44,6 +44,7 @@ private slots: void pause(); void resume(); void openFolder(); + void openFile(); private: void setSize(uint64_t size); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui index 39e32271d..c1b02ab83 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelFilesStatusWidget.ui @@ -48,7 +48,7 @@ 2 - + 0 @@ -126,18 +126,31 @@ - + 0 0 + + Play + + + + + + + + 0 + 0 + + Qt::NoFocus - - Open folder + + QToolButton::InstantPopup @@ -148,6 +161,7 @@
+ diff --git a/retroshare-gui/src/gui/icons.qrc b/retroshare-gui/src/gui/icons.qrc index 6e4bd7569..656cdc7fd 100644 --- a/retroshare-gui/src/gui/icons.qrc +++ b/retroshare-gui/src/gui/icons.qrc @@ -60,6 +60,7 @@ icons/png/anonymous.png icons/png/attach-image.png icons/png/attach.png + icons/png/arrow.png icons/png/cert.png icons/png/channels-notify.png icons/png/channels.png diff --git a/retroshare-gui/src/gui/icons/png/arrow.png b/retroshare-gui/src/gui/icons/png/arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..e238538026d1e88b3f373287ead27e503f5c161d GIT binary patch literal 935 zcmeAS@N?(olHy`uVBq!ia0vp^4Is?H3?#oinD`S&v7|ftIx;Y9?C1WI$O_~uBzpw; zGB8xB0oAoIF#H0kf5E^|YQVtoDuIE)Y6b&?c)^@qfi^&iMFBn`u0Wb$)qm#M*O+Ht zWtx2zNHR41XP9}3Y4$aySyx$Sg2Y+pTxFPc3CIA+&OgsG`zjDYxwEb_%(%uf=L*ZL z%WN~Rv(387F!Kyls_!v~G3N@y?8{J*xi^^SfHX4AxxzH(D$sneI9PVpHHMjYftE7Q zx&}0y3CLY?8z>8O-HvEab0B3>666=mz#P8n@?9p4ix-UE8wx&tB+%9V{DqpTwUw?m z*S9a6E0=5j{GpzdSXf!~FX&Yuf5Fb{`FuN0{n!N5&zR)x?!wT)D(eB{a29w(7BevL z9RXp+soH$fKtc8rPhVH|Cv02-W^(6FzMKZs)8Xmj7*cWT?bYxgCP$IhhhhRDXDrnX z8dfYQ|DXN2QSwm>pLs~+^PC62<*h8*6D8W9=thZjZ(4S6?%K4)d*76A^QgQ%*R_86 z>bJtB<uG!l<>snA z+9VXecEOc!trZ_axwL9qo226REVw4F_u+#p_nw-AO#bm}4%`;k;`rdo)l}2i7) z!i{vjjt`OCi)xNFdCXs{a4TKQ<3l9Zq?)!S7yF$GH~n=IK1}9{_|w9qX1}Z9Y`;#z zm&IHWe><4c>~}QW<<}AT(9GrXr-4cF{%(ge-*p7O?B#N)?Pp5dzs=#!dd+|j+T0<3 zx|me=Z*e%QuNm-#n>*xh6H{vaT8F#wng$={O7j%gS#0<)_qT-a-;2w8-`~0Zy~0TH z|DULz@UWElX8(=-M$jeR3Y{f~fzhN|;u=wsl30>zm0Xkxq!^40j0|)Q%ybP6LJZBU zOiiqe4YUmmtPBk9+E2cOq9HdwB{QuOw}v%I+Ic_?k{}y`^V3So6N^$A%FE03GV`*F clM@S4_413-XTP(N0xDwgboFyt=akR{0M4y>Jpcdz literal 0 HcmV?d00001 From 8a636024d6260185c97730bf2fe1fba94452d61a Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 11 Jun 2020 20:09:24 +0200 Subject: [PATCH 67/89] forget this to commit --- retroshare-gui/src/gui/qss/stylesheet/Standard.qss | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss index 53ac80a44..505fc83c9 100644 --- a/retroshare-gui/src/gui/qss/stylesheet/Standard.qss +++ b/retroshare-gui/src/gui/qss/stylesheet/Standard.qss @@ -791,6 +791,14 @@ GxsChannelPostsWidget QToolButton#subscribeToolButton::menu-button { } +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton::menu-indicator { + image: none; +} + +GxsChannelFilesStatusWidget QToolButton#openFolderToolButton[popupMode="0"] { + padding-right: 0px; +} + GxsGroupDialog QLabel#groupLogo{ border: 2px solid #CCCCCC; border-radius: 3px; From 0cb0678dc5f3f2a59860cefacdb48a6b0702fc60 Mon Sep 17 00:00:00 2001 From: defnax Date: Sun, 14 Jun 2020 15:06:04 +0200 Subject: [PATCH 68/89] Fixing margins & fix filter linedit to use LineEditClear class --- .../GxsChannelPostsWidgetWithModel.ui | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 097f6b298..ef40e8713 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -128,7 +128,7 @@
- + 0 @@ -358,6 +358,9 @@ p, li { white-space: pre-wrap; } Posts + + 0 + @@ -409,6 +412,9 @@ p, li { white-space: pre-wrap; } Details + + 3 + @@ -492,6 +498,18 @@ p, li { white-space: pre-wrap; } Comments + + 0 + + + 0 + + + 0 + + + 0 + @@ -566,6 +584,11 @@ p, li { white-space: pre-wrap; }
gui/gxs/GxsCommentDialog.h
1 + + LineEditClear + QLineEdit +
gui/common/LineEditClear.h
+
From b3b87303e2ea716880d0b23c76d1b65d125497ae Mon Sep 17 00:00:00 2001 From: defnax Date: Wed, 17 Jun 2020 00:21:32 +0200 Subject: [PATCH 69/89] Added default column with for file name & store the column state & width * Added default column width for file name & store the column state & width * disabled resize column to contents --- .../GxsChannelPostsWidgetWithModel.cpp | 26 +++++++++++++++---- 1 file changed, 21 insertions(+), 5 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 1560eaf4f..90b820d4a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -266,7 +266,12 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI connect(ui->postsTree,SIGNAL(sizeChanged(QSize)),this,SLOT(handlePostsTreeSizeChange(QSize))); - //ui->nameLabel->setMinimumWidth(20); + /* Set initial section sizes */ + QHeaderView * channelpostfilesheader = ui->channelPostFiles_TV->header () ; + QHeaderView * channelfilesheader = ui->channelFiles_TV->header () ; + + channelpostfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")*1.5); + channelfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")*1.5); /* Initialize feed widget */ //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); @@ -421,8 +426,8 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->postTime_LB->setText(QDateTime::fromMSecsSinceEpoch(post.mMeta.mPublishTs*1000).toString("MM/dd/yyyy, hh:mm")); ui->postTime_LB->setFixedWidth(W); - ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); - ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); + //ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); + //ui->channelPostFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelPostFiles_TV->setAutoSelect(true); // Now also set the post as read @@ -484,8 +489,8 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() mChannelPostsModel->getFilesList(files); mChannelFilesModel->setFiles(files); - ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); - ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); + //ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); + //ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelFiles_TV->setAutoSelect(true); @@ -536,6 +541,9 @@ GxsChannelPostsWidgetWithModel::~GxsChannelPostsWidgetWithModel() void GxsChannelPostsWidgetWithModel::processSettings(bool load) { + QHeaderView *channelpostfilesheader = ui->channelPostFiles_TV->header () ; + QHeaderView *channelfilesheader = ui->channelFiles_TV->header () ; + Settings->beginGroup(QString("ChannelPostsWidget")); if (load) { @@ -548,6 +556,10 @@ void GxsChannelPostsWidgetWithModel::processSettings(bool load) /* View mode */ //setViewMode(Settings->value("viewMode", VIEW_MODE_FEEDS).toInt()); #endif + // state of files tree + channelpostfilesheader->restoreState(Settings->value("PostFilesTree").toByteArray()); + channelfilesheader->restoreState(Settings->value("FilesTree").toByteArray()); + // state of splitter ui->splitter->restoreState(Settings->value("SplitterChannelPosts").toByteArray()); } else { @@ -560,6 +572,10 @@ void GxsChannelPostsWidgetWithModel::processSettings(bool load) /* View mode */ //Settings->setValue("viewMode", viewMode()); #endif + // state of files tree + Settings->setValue("PostFilesTree", channelpostfilesheader->saveState()); + Settings->setValue("FilesTree", channelfilesheader->saveState()); + // state of splitter Settings->setValue("SplitterChannelPosts", ui->splitter->saveState()); } From 9e0c24767735c08994c362572b1150ad6ba42d85 Mon Sep 17 00:00:00 2001 From: defnax Date: Wed, 17 Jun 2020 17:19:12 +0200 Subject: [PATCH 70/89] Fixing get work links click on messages & clear the channel details when no channel selected --- .../GxsChannelPostsWidgetWithModel.cpp | 14 +- .../GxsChannelPostsWidgetWithModel.ui | 136 +++++++++--------- 2 files changed, 81 insertions(+), 69 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 90b820d4a..10de9b504 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -955,10 +955,18 @@ void GxsChannelPostsWidgetWithModel::blank() ui->postButton->setEnabled(false); ui->subscribeToolButton->setEnabled(false); - mChannelPostsModel->clear(); - groupNameChanged(QString()); + ui->channelName_LB->setText(tr("No Channel Selected")); + ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channels.png")); + ui->infoPosts->setText(""); + ui->infoLastPost->setText(""); + ui->infoAdministrator->setText(""); + ui->infoDistribution->setText(""); + ui->infoCreated->setText(""); + ui->infoDescription->setText(""); + + mChannelPostsModel->clear(); + groupNameChanged(QString()); - //ui->infoWidget->hide(); } bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId &msgId) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 097f6b298..0a7f3a594 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -161,7 +161,7 @@ - 1 + 0 @@ -199,25 +199,8 @@ - - - - unknown - - - true - - - - - - - unknown - - - - - + + 75 @@ -225,33 +208,14 @@ - Created: + Distribution: - - + + - unknown - - - - - - - - 0 - 0 - - - - - 75 - true - - - - Posts: + unknown @@ -268,21 +232,8 @@
- - - - 0 - - - - - - - - 0 - 0 - - + + 75 @@ -290,14 +241,31 @@ - Last Post: + Created: - - + + - unknown + unknown + + + + + + + 0 + + + + + + + unknown + + + true @@ -314,8 +282,21 @@
- - + + + + unknown + + + + + + + + 0 + 0 + + 75 @@ -323,7 +304,26 @@ - Distribution: + Posts: + + + + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: @@ -460,7 +460,11 @@ p, li { white-space: pre-wrap; } - + + + true + +
From 9c72797deebf898adb13cc5cdd7af37399e3c6ed Mon Sep 17 00:00:00 2001 From: csoler Date: Wed, 17 Jun 2020 20:30:05 +0200 Subject: [PATCH 71/89] removed 1.5 factor that did not really make sense in column size definition --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 10de9b504..2afd6ccf0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -270,8 +270,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI QHeaderView * channelpostfilesheader = ui->channelPostFiles_TV->header () ; QHeaderView * channelfilesheader = ui->channelFiles_TV->header () ; - channelpostfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")*1.5); - channelfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")*1.5); + channelpostfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); + channelfilesheader->resizeSection (RsGxsChannelPostFilesModel::COLUMN_FILES_NAME, fm.width("RetroShare-v0.6.5-1487-g6714648e5-Windows-x64-portable-20200518-Qt-5.14.2.7z")); /* Initialize feed widget */ //ui->feedWidget->setSortRole(ROLE_PUBLISH, Qt::DescendingOrder); From fed3dee1d70d152d7dd1ac10e1867040f0a32509 Mon Sep 17 00:00:00 2001 From: defnax Date: Wed, 17 Jun 2020 22:47:44 +0200 Subject: [PATCH 72/89] Clear more fields when blank tree is selected * Clear more fields when blank tree is selected * Fixing spacing * Show different placeholder when message contains no post --- .../GxsChannelPostsWidgetWithModel.cpp | 16 ++- .../GxsChannelPostsWidgetWithModel.ui | 106 +++++++++--------- 2 files changed, 67 insertions(+), 55 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 2afd6ccf0..7bf0bedf1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -373,6 +373,12 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() { QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + QTextDocument doc; + doc.setHtml(post.mMsg.c_str()); + + if(doc.toPlainText().trimmed().isEmpty()) + ui->postDetails_TE->setPlaceholderText(tr("No message in this post")); if(post.mMeta.mPublishTs == 0) { @@ -492,7 +498,7 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() //ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); //ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelFiles_TV->setAutoSelect(true); - + ui->postDetails_TE->setPlaceholderText(tr("No post selected")); } @@ -954,7 +960,7 @@ void GxsChannelPostsWidgetWithModel::blank() { ui->postButton->setEnabled(false); ui->subscribeToolButton->setEnabled(false); - + ui->channelName_LB->setText(tr("No Channel Selected")); ui->logoLabel->setPixmap(FilesDefs::getPixmapFromQtResourcePath(":/icons/png/channels.png")); ui->infoPosts->setText(""); @@ -965,6 +971,12 @@ void GxsChannelPostsWidgetWithModel::blank() ui->infoDescription->setText(""); mChannelPostsModel->clear(); + mChannelPostFilesModel->clear(); + ui->postDetails_TE->clear(); + ui->postDetails_TE->setPlaceholderText(tr("No post selected")); + ui->postLogo_LB->hide(); + ui->postName_LB->hide(); + ui->postTime_LB->hide(); groupNameChanged(QString()); } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index bb12239f2..28687b2df 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -199,19 +199,6 @@
- - - - - 75 - true - - - - Distribution: - - - @@ -219,19 +206,6 @@ - - - - Qt::Horizontal - - - - 40 - 20 - - - - @@ -245,11 +219,27 @@ - - + + unknown + + true + + + + + + + + 75 + true + + + + Distribution: + @@ -259,14 +249,30 @@ - - + + + + + 0 + 0 + + + + + 75 + true + + + + Last Post: + + + + + unknown - - true - @@ -292,7 +298,7 @@ - + 0 0 @@ -308,27 +314,21 @@ - - - - - 0 - 0 - - - - - 75 - true - - - - Last Post: - - - + + + + Qt::Horizontal + + + + 40 + 20 + + + + From 3354246805f7de7bbe542b18d325afbcf448c00e Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 18 Jun 2020 18:31:17 +0200 Subject: [PATCH 73/89] added publish time column in general channel posts files --- .../gxschannels/GxsChannelPostFilesModel.cpp | 26 ++++++++------- .../gxschannels/GxsChannelPostFilesModel.h | 32 +++++++++++++------ .../gui/gxschannels/GxsChannelPostsModel.cpp | 17 ++++++---- .../gui/gxschannels/GxsChannelPostsModel.h | 5 ++- .../GxsChannelPostsWidgetWithModel.cpp | 21 ++++++++---- 5 files changed, 67 insertions(+), 34 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 6b4a4b14d..664071ae0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -35,7 +35,7 @@ //#define DEBUG_CHANNEL_MODEL -Q_DECLARE_METATYPE(RsGxsFile) +Q_DECLARE_METATYPE(ChannelPostFileInfo) static std::ostream& operator<<(std::ostream& o, const QModelIndex& i);// defined elsewhere @@ -106,7 +106,7 @@ int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const // return std::vector >(); // } -bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,RsGxsFile& fmpe) const +bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,ChannelPostFileInfo& fmpe) const { if(!i.isValid()) return true; @@ -249,6 +249,7 @@ QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation ori case COLUMN_FILES_FILE: return QString("Status"); case COLUMN_FILES_SIZE: return QString("Size"); case COLUMN_FILES_NAME: return QString("File"); + case COLUMN_FILES_DATE: return QString("Publish date"); default: return QString("[No data]"); } @@ -293,7 +294,7 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co return QVariant() ; } - const RsGxsFile& fmpe(mFiles[mFilteredFiles[entry]]); + const ChannelPostFileInfo& fmpe(mFiles[mFilteredFiles[entry]]); #ifdef TODO if(role == Qt::FontRole) @@ -376,13 +377,14 @@ class compareOperator public: compareOperator(int column,Qt::SortOrder order): col(column),ord(order) {} - bool operator()(const RsGxsFile& f1,const RsGxsFile& f2) const + bool operator()(const ChannelPostFileInfo& f1,const ChannelPostFileInfo& f2) const { switch(col) { default: case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return (ord==Qt::AscendingOrder)?(f1.mNamef2.mName); case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return (ord==Qt::AscendingOrder)?(f1.mSizef2.mSize); + case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: return (ord==Qt::AscendingOrder)?(f1.mPublishTimef2.mPublishTime); case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: { FileInfo fi1,fi2; @@ -579,12 +581,13 @@ QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const #endif } -QVariant RsGxsChannelPostFilesModel::sortRole(const RsGxsFile& fmpe,int column) const +QVariant RsGxsChannelPostFilesModel::sortRole(const ChannelPostFileInfo& fmpe,int column) const { switch(column) { case COLUMN_FILES_NAME: return QVariant(QString::fromUtf8(fmpe.mName.c_str())); case COLUMN_FILES_SIZE: return QVariant(qulonglong(fmpe.mSize)); + case COLUMN_FILES_DATE: return QVariant(qulonglong(fmpe.mPublishTime)); case COLUMN_FILES_FILE: { FileInfo finfo; @@ -600,13 +603,14 @@ QVariant RsGxsChannelPostFilesModel::sortRole(const RsGxsFile& fmpe,int column) } } -QVariant RsGxsChannelPostFilesModel::displayRole(const RsGxsFile& fmpe,int col) const +QVariant RsGxsChannelPostFilesModel::displayRole(const ChannelPostFileInfo& fmpe,int col) const { switch(col) { - case 0: return QString::fromUtf8(fmpe.mName.c_str()); - case 1: return QString::number(fmpe.mSize); - case 2: { + case COLUMN_FILES_NAME: return QString::fromUtf8(fmpe.mName.c_str()); + case COLUMN_FILES_SIZE: return QString::number(fmpe.mSize); + case COLUMN_FILES_DATE: return QString::number(fmpe.mPublishTime); + case COLUMN_FILES_FILE: { FileInfo finfo; if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) return qulonglong(finfo.transfered); @@ -659,7 +663,7 @@ QVariant RsGxsChannelPostFilesModel::displayRole(const RsGxsFile& fmpe,int col) return QVariant("[ERROR]"); } -QVariant RsGxsChannelPostFilesModel::userRole(const RsGxsFile& fmpe,int col) const +QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,int col) const { switch(col) { @@ -695,7 +699,7 @@ void RsGxsChannelPostFilesModel::clear() emit channelLoaded(); } -void RsGxsChannelPostFilesModel::setFiles(const std::list& files) +void RsGxsChannelPostFilesModel::setFiles(const std::list &files) { preMods(); diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h index 4f0e432ff..d2d4fabf8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.h @@ -36,6 +36,19 @@ typedef uint32_t ChannelPostFilesModelIndex; class QTimer; +// This class contains the info for a file as well as additional info such as publication date + +struct ChannelPostFileInfo: public RsGxsFile +{ + ChannelPostFileInfo(const RsGxsFile& gxs_file,rstime_t t) + : RsGxsFile(gxs_file),mPublishTime(t) + {} + + ChannelPostFileInfo() : mPublishTime(0) {} + + rstime_t mPublishTime; +}; + // This class is the item model used by Qt to display the information class RsGxsChannelPostFilesModel : public QAbstractItemModel @@ -50,7 +63,8 @@ public: COLUMN_FILES_NAME = 0x00, COLUMN_FILES_SIZE = 0x01, COLUMN_FILES_FILE = 0x02, - COLUMN_FILES_NB_COLUMNS = 0x03 + COLUMN_FILES_DATE = 0x03, + COLUMN_FILES_NB_COLUMNS = 0x04 }; enum Roles{ SortRole = Qt::UserRole+1, @@ -66,7 +80,7 @@ public: QModelIndex root() const{ return createIndex(0,0,(void*)NULL) ;} // This method will asynchroneously update the data - void setFiles(const std::list& files); + void setFiles(const std::list& files); void setFilter(const QStringList &strings, uint32_t &count) ; #ifdef TODO @@ -102,11 +116,11 @@ public: // Custom item roles QVariant sizeHintRole (int col) const; - QVariant displayRole (const RsGxsFile& fmpe, int col) const; - QVariant toolTipRole (const RsGxsFile& fmpe, int col) const; - QVariant userRole (const RsGxsFile& fmpe, int col) const; - QVariant sortRole (const RsGxsFile& fmpe, int col) const; - QVariant filterRole (const RsGxsFile& fmpe, int col) const; + QVariant displayRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant toolTipRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant userRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant sortRole (const ChannelPostFileInfo& fmpe, int col) const; + QVariant filterRole (const ChannelPostFileInfo& fmpe, int col) const; #ifdef TODO QVariant decorationRole(const ForumModelPostEntry& fmpe, int col) const; QVariant pinnedRole (const ForumModelPostEntry& fmpe, int col) const; @@ -142,7 +156,7 @@ private: quintptr getParentRow(quintptr ref,int& row) const; quintptr getChildRef(quintptr ref, int index) const; int getChildrenCount(quintptr ref) const; - bool getFileData(const QModelIndex& i,RsGxsFile& fmpe) const; + bool getFileData(const QModelIndex& i, ChannelPostFileInfo &fmpe) const; static bool convertTabEntryToRefPointer(uint32_t entry, quintptr& ref); static bool convertRefPointerToTabEntry(quintptr ref,uint32_t& entry); @@ -153,7 +167,7 @@ private: void initEmptyHierarchy(); std::vector mFilteredFiles ; // store the list of files for the post - std::vector mFiles ; // store the list of files for the post + std::vector mFiles ; // store the list of files for the post QTimer *mTimer; }; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 9c40f97e3..4732d93c1 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -23,14 +23,17 @@ #include #include +#include "retroshare/rsgxsflags.h" +#include "retroshare/rsgxschannels.h" +#include "retroshare/rsexpr.h" + #include "gui/common/FilesDefs.h" #include "util/qtthreadsutils.h" #include "util/HandleRichText.h" #include "util/DateTime.h" + #include "GxsChannelPostsModel.h" -#include "retroshare/rsgxsflags.h" -#include "retroshare/rsgxschannels.h" -#include "retroshare/rsexpr.h" +#include "GxsChannelPostFilesModel.h" //#define DEBUG_CHANNEL_MODEL @@ -142,15 +145,15 @@ void RsGxsChannelPostsModel::postMods() emit dataChanged(createIndex(0,0,(void*)NULL), createIndex(mFilteredPosts.size(),mColumns-1,(void*)NULL)); } -void RsGxsChannelPostsModel::getFilesList(std::list& files) +void RsGxsChannelPostsModel::getFilesList(std::list& files) { // We use an intermediate map so as to remove duplicates - std::map files_map; + std::map files_map; - for(uint32_t i=1;i #include +struct ChannelPostFileInfo; + // This class holds the actual hierarchy of posts, represented by identifiers // It is responsible for auto-updating when necessary and holds a mutex to allow the Model to // safely access the data. @@ -112,7 +115,7 @@ public: // Retrieve the full list of files for all posts. - void getFilesList(std::list& files); + void getFilesList(std::list &files); #ifdef TODO void setSortMode(SortMode mode) ; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 2afd6ccf0..5d3702f9a 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -72,7 +72,7 @@ static const int CHANNEL_TABS_POSTS = 1; #define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png" -Q_DECLARE_METATYPE(RsGxsFile) +Q_DECLARE_METATYPE(ChannelPostFileInfo) // Delegate used to paint into the table of thumbnails @@ -131,7 +131,7 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const { - RsGxsFile file = index.data(Qt::UserRole).value() ; + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; if(index.column() == RsGxsChannelPostFilesModel::COLUMN_FILES_FILE) return new GxsChannelFilesStatusWidget(file,parent); @@ -145,7 +145,7 @@ void ChannelPostFilesDelegate::updateEditorGeometry(QWidget *editor, const QStyl void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { - RsGxsFile file = index.data(Qt::UserRole).value() ; + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; // prepare painter->save(); @@ -163,6 +163,8 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI break; case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: painter->drawText(option.rect,Qt::AlignRight | Qt::AlignVCenter,misc::friendlyUnit(qulonglong(file.mSize))); break; + case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: painter->drawText(option.rect,Qt::AlignLeft | Qt::AlignVCenter,QDateTime::fromMSecsSinceEpoch(file.mPublishTime*1000).toString("MM/dd/yyyy, hh:mm")); + break; case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: { GxsChannelFilesStatusWidget w(file); @@ -186,7 +188,7 @@ void ChannelPostFilesDelegate::paint(QPainter * painter, const QStyleOptionViewI QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const { - RsGxsFile file = index.data(Qt::UserRole).value() ; + ChannelPostFileInfo file = index.data(Qt::UserRole).value() ; QFontMetricsF fm(option.font); @@ -194,6 +196,7 @@ QSize ChannelPostFilesDelegate::sizeHint(const QStyleOptionViewItem& option, con { case RsGxsChannelPostFilesModel::COLUMN_FILES_NAME: return QSize(1.1*fm.width(QString::fromUtf8(file.mName.c_str())),fm.height()); case RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE: return QSize(1.1*fm.width(misc::friendlyUnit(qulonglong(file.mSize))),fm.height()); + case RsGxsChannelPostFilesModel::COLUMN_FILES_DATE: return QSize(1.1*fm.width(QDateTime::fromMSecsSinceEpoch(file.mPublishTime*1000).toString("MM/dd/yyyy, hh:mm")),fm.height()); default: case RsGxsChannelPostFilesModel::COLUMN_FILES_FILE: return QSize(option.rect.width(),GxsChannelFilesStatusWidget(file).height()); } @@ -280,6 +283,8 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI /* load settings */ processSettings(true); + ui->channelPostFiles_TV->setColumnHidden(RsGxsChannelPostFilesModel::COLUMN_FILES_DATE, true); // no need to show this here. + /* Initialize subscribe button */ QIcon icon; icon.addPixmap(QPixmap(":/images/redled.png"), QIcon::Normal, QIcon::On); @@ -396,7 +401,11 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() mSelectedGroup = mGroup.mMeta.mGroupId; mSelectedPost = post.mMeta.mMsgId; - mChannelPostFilesModel->setFiles(post.mFiles); + std::list files; + for(auto& file:post.mFiles) + files.push_back(ChannelPostFileInfo(file,post.mMeta.mPublishTs)); + + mChannelPostFilesModel->setFiles(files); auto all_msgs_versions(post.mOlderVersions); all_msgs_versions.insert(post.mMeta.mMsgId); @@ -484,7 +493,7 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() whileBlocking(ui->postsTree)->setCurrentIndex(index); } - std::list files; + std::list files; mChannelPostsModel->getFilesList(files); mChannelFilesModel->setFiles(files); From 3df54bde53919991b0129a89226e979a7ae5f11f Mon Sep 17 00:00:00 2001 From: csoler Date: Thu, 18 Jun 2020 18:35:02 +0200 Subject: [PATCH 74/89] cleaned up unused code --- .../gxschannels/GxsChannelPostFilesModel.cpp | 381 +----------------- .../gui/gxschannels/GxsChannelPostsModel.cpp | 5 - 2 files changed, 13 insertions(+), 373 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 664071ae0..28bdb0e37 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -96,16 +96,6 @@ int RsGxsChannelPostFilesModel::columnCount(const QModelIndex &/*parent*/) const return COLUMN_FILES_NB_COLUMNS ; } -// std::vector > RsGxsChannelPostsModel::getPostVersions(const RsGxsMessageId& mid) const -// { -// auto it = mPostVersions.find(mid); -// -// if(it != mPostVersions.end()) -// return it->second; -// else -// return std::vector >(); -// } - bool RsGxsChannelPostFilesModel::getFileData(const QModelIndex& i,ChannelPostFileInfo& fmpe) const { if(!i.isValid()) @@ -296,38 +286,11 @@ QVariant RsGxsChannelPostFilesModel::data(const QModelIndex &index, int role) co const ChannelPostFileInfo& fmpe(mFiles[mFilteredFiles[entry]]); -#ifdef TODO - if(role == Qt::FontRole) - { - QFont font ; - font.setBold( (fmpe.mPostFlags & (ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN | ForumModelPostEntry::FLAG_POST_IS_PINNED)) || IS_MSG_UNREAD(fmpe.mMsgStatus)); - return QVariant(font); - } - - if(role == UnreadChildrenRole) - return bool(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_HAS_UNREAD_CHILDREN); - -#ifdef DEBUG_CHANNEL_MODEL - std::cerr << " [ok]" << std::endl; -#endif -#endif - switch(role) { case Qt::DisplayRole: return displayRole (fmpe,index.column()) ; case Qt::UserRole: return userRole (fmpe,index.column()) ; case SortRole: return sortRole (fmpe,index.column()) ; -#ifdef TODO - case Qt::DecorationRole: return decorationRole(fmpe,index.column()) ; - case Qt::ToolTipRole: return toolTipRole (fmpe,index.column()) ; - case Qt::TextColorRole: return textColorRole (fmpe,index.column()) ; - case Qt::BackgroundRole: return backgroundRole(fmpe,index.column()) ; - - case FilterRole: return filterRole (fmpe,index.column()) ; - case ThreadPinnedRole: return pinnedRole (fmpe,index.column()) ; - case MissingRole: return missingRole (fmpe,index.column()) ; - case StatusRole: return statusRole (fmpe,index.column()) ; -#endif default: return QVariant(); } @@ -409,176 +372,11 @@ void RsGxsChannelPostFilesModel::sort(int column, Qt::SortOrder order) update(); } -#ifdef TODO -QVariant RsGxsForumModel::textColorRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if( (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING)) - return QVariant(mTextColorMissing); - - if(IS_MSG_UNREAD(fmpe.mMsgStatus) || (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED)) - return QVariant(mTextColorUnread); - else - return QVariant(mTextColorRead); - - return QVariant(); -} - -QVariant RsGxsForumModel::statusRole(const ForumModelPostEntry& fmpe,int column) const -{ - if(column != COLUMN_THREAD_DATA) - return QVariant(); - - return QVariant(fmpe.mMsgStatus); -} - -uint32_t RsGxsForumModel::recursUpdateFilterStatus(ForumModelIndex i,int column,const QStringList& strings) -{ - QString s ; - uint32_t count = 0; - - switch(column) - { - default: - case COLUMN_THREAD_DATE: - case COLUMN_THREAD_TITLE: s = displayRole(mPosts[i],column).toString(); - break; - case COLUMN_THREAD_AUTHOR: - { - QString comment ; - QList icons; - - GxsIdDetails::MakeIdDesc(mPosts[i].mAuthorId, false,s, icons, comment,GxsIdDetails::ICON_TYPE_NONE); - } - break; - } - - if(!strings.empty()) - { - mPosts[i].mPostFlags &= ~(ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER); - - for(auto iter(strings.begin()); iter != strings.end(); ++iter) - if(s.contains(*iter,Qt::CaseInsensitive)) - { - mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER | ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; - - count++; - break; - } - } - else - { - mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_PASSES_FILTER |ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; - count++; - } - - for(uint32_t j=0;j 0) - mPosts[i].mPostFlags |= ForumModelPostEntry::FLAG_POST_CHILDREN_PASSES_FILTER; - } - - return count; -} - - -void RsGxsForumModel::setFilter(int column,const QStringList& strings,uint32_t& count) -{ - preMods(); - - if(!strings.empty()) - { - count = recursUpdateFilterStatus(ForumModelIndex(0),column,strings); - mFilteringEnabled = true; - } - else - { - count=0; - mFilteringEnabled = false; - } - - postMods(); -} - -QVariant RsGxsForumModel::missingRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) - return QVariant(true); - else - return QVariant(false); -} - -QVariant RsGxsForumModel::toolTipRole(const ForumModelPostEntry& fmpe,int column) const -{ - if(column == COLUMN_THREAD_DISTRIBUTION) - switch(fmpe.mReputationWarningLevel) - { - case 3: return QVariant(tr("Information for this identity is currently missing.")) ; - case 2: return QVariant(tr("You have banned this ID. The message will not be\ndisplayed nor forwarded to your friends.")) ; - case 1: return QVariant(tr("You have not set an opinion for this person,\n and your friends do not vote positively: Spam regulation \nprevents the message to be forwarded to your friends.")) ; - case 0: return QVariant(tr("Message will be forwarded to your friends.")) ; - default: - return QVariant("[ERROR: missing reputation level information - contact the developers]"); - } - - if(column == COLUMN_THREAD_AUTHOR) - { - QString str,comment ; - QList icons; - - if(!GxsIdDetails::MakeIdDesc(fmpe.mAuthorId, true, str, icons, comment,GxsIdDetails::ICON_TYPE_AVATAR)) - return QVariant(); - - int S = QFontMetricsF(QApplication::font()).height(); - QImage pix( (*icons.begin()).pixmap(QSize(4*S,4*S)).toImage()); - - QString embeddedImage; - if(RsHtml::makeEmbeddedImage(pix.scaled(QSize(4*S,4*S), Qt::KeepAspectRatio, Qt::SmoothTransformation), embeddedImage, 8*S * 8*S)) - comment = "
" + embeddedImage + "" + comment + "
"; - - return comment; - } - - return QVariant(); -} - -QVariant RsGxsForumModel::pinnedRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) - return QVariant(true); - else - return QVariant(false); -} - -QVariant RsGxsForumModel::backgroundRole(const ForumModelPostEntry& fmpe,int /*column*/) const -{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) - return QVariant(QBrush(QColor(255,200,180))); - - if(mFilteringEnabled && (fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_PASSES_FILTER)) - return QVariant(QBrush(QColor(255,240,210))); - - return QVariant(); -} -#endif - QVariant RsGxsChannelPostFilesModel::sizeHintRole(int col) const { float factor = QFontMetricsF(QApplication::font()).height()/14.0f ; return QVariant( QSize(factor * 170, factor*14 )); -#ifdef TODO - switch(col) - { - default: - case COLUMN_THREAD_TITLE: return QVariant( QSize(factor * 170, factor*14 )); - case COLUMN_THREAD_DATE: return QVariant( QSize(factor * 75 , factor*14 )); - case COLUMN_THREAD_AUTHOR: return QVariant( QSize(factor * 75 , factor*14 )); - case COLUMN_THREAD_DISTRIBUTION: return QVariant( QSize(factor * 15 , factor*14 )); - } -#endif } QVariant RsGxsChannelPostFilesModel::sortRole(const ChannelPostFileInfo& fmpe,int column) const @@ -607,57 +405,20 @@ QVariant RsGxsChannelPostFilesModel::displayRole(const ChannelPostFileInfo& fmpe { switch(col) { - case COLUMN_FILES_NAME: return QString::fromUtf8(fmpe.mName.c_str()); - case COLUMN_FILES_SIZE: return QString::number(fmpe.mSize); - case COLUMN_FILES_DATE: return QString::number(fmpe.mPublishTime); - case COLUMN_FILES_FILE: { - FileInfo finfo; - if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) - return qulonglong(finfo.transfered); - else - return 0; - } - default: - return QString(); -#ifdef TODO - case COLUMN_THREAD_TITLE: if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_REDACTED) - return QVariant(tr("[ ... Redacted message ... ]")); - else if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_PINNED) - return QVariant(tr("[PINNED] ") + QString::fromUtf8(fmpe.mTitle.c_str())); - else - return QVariant(QString::fromUtf8(fmpe.mTitle.c_str())); - - case COLUMN_THREAD_READ:return QVariant(); - case COLUMN_THREAD_DATE:{ - if(fmpe.mPostFlags & ForumModelPostEntry::FLAG_POST_IS_MISSING) - return QVariant(QString()); - - QDateTime qtime; - qtime.setTime_t(fmpe.mPublishTs); - - return QVariant(DateTime::formatDateTime(qtime)); - } - - case COLUMN_THREAD_DISTRIBUTION: - case COLUMN_THREAD_AUTHOR:{ - QString name; - RsGxsId id = RsGxsId(fmpe.mAuthorId.toStdString()); - - if(id.isNull()) - return QVariant(tr("[Notification]")); - if(GxsIdTreeItemDelegate::computeName(id,name)) - return name; - return QVariant(tr("[Unknown]")); - } - case COLUMN_THREAD_MSGID: return QVariant(); - if (filterColumn == COLUMN_THREAD_CONTENT) { - // need content for filter - QTextDocument doc; - doc.setHtml(QString::fromUtf8(msg.mMsg.c_str())); - item->setText(COLUMN_THREAD_CONTENT, doc.toPlainText().replace(QString("\n"), QString(" "))); + case COLUMN_FILES_NAME: return QString::fromUtf8(fmpe.mName.c_str()); + case COLUMN_FILES_SIZE: return QString::number(fmpe.mSize); + case COLUMN_FILES_DATE: return QString::number(fmpe.mPublishTime); + case COLUMN_FILES_FILE: { + FileInfo finfo; + if(rsFiles->FileDetails(fmpe.mHash,RS_FILE_HINTS_DOWNLOAD,finfo)) + return qulonglong(finfo.transfered); + else + return 0; + } + default: + return QString(); + } -#endif - } return QVariant("[ERROR]"); @@ -672,23 +433,6 @@ QVariant RsGxsChannelPostFilesModel::userRole(const ChannelPostFileInfo& fmpe,in } } -#ifdef TODO -QVariant RsGxsForumModel::decorationRole(const ForumModelPostEntry& fmpe,int col) const -{ - bool exist=false; - switch(col) - { - case COLUMN_THREAD_DISTRIBUTION: - return QVariant(fmpe.mReputationWarningLevel); - case COLUMN_THREAD_READ: - return QVariant(fmpe.mMsgStatus); - case COLUMN_THREAD_AUTHOR://Return icon as place holder. - return FilesDefs::getIconFromGxsIdCache(RsGxsId(fmpe.mAuthorId.toStdString()),QIcon(), exist); - } - return QVariant(); -} -#endif - void RsGxsChannelPostFilesModel::clear() { preMods(); @@ -714,11 +458,6 @@ void RsGxsChannelPostFilesModel::setFiles(const std::list & for(uint32_t i=0;i & else mTimer->stop(); } - -#ifdef DEBUG_FORUMMODEL -QModelIndex RsGxsChannelPostFilesModel::getIndexOfFile(const RsFileHash& hash) const -{ - // Brutal search. This is not so nice, so dont call that in a loop! If too costly, we'll use a map. - - for(uint32_t i=1;i& entries,ForumModelIndex index,int depth) -{ - const ForumModelPostEntry& e(entries[index]); - - QDateTime qtime; - qtime.setTime_t(e.mPublishTs); - - std::cerr << std::string(depth*2,' ') << index << " : " << e.mAuthorId.toStdString() << " " - << QString("%1").arg((uint32_t)e.mPostFlags,8,16,QChar('0')).toStdString() << " " - << QString("%1").arg((uint32_t)e.mMsgStatus,8,16,QChar('0')).toStdString() << " " - << qtime.toString().toStdString() << " \"" << e.mTitle << "\"" << std::endl; - - for(uint32_t i=0;i= mPosts.size()) - return ; - - std::cerr << "Setting own opinion for author " << mPosts[entry].mAuthorId - << " to " << static_cast(op) << std::endl; - RsGxsId author_id = mPosts[entry].mAuthorId; - - rsReputations->setOwnOpinion(author_id,op) ; - - // update opinions and distribution flags. No need to re-load all posts. - - for(uint32_t i=0;i Date: Thu, 18 Jun 2020 18:54:43 +0200 Subject: [PATCH 75/89] using shorter name for publish column --- retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp index 28bdb0e37..987144162 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostFilesModel.cpp @@ -239,7 +239,7 @@ QVariant RsGxsChannelPostFilesModel::headerData(int section, Qt::Orientation ori case COLUMN_FILES_FILE: return QString("Status"); case COLUMN_FILES_SIZE: return QString("Size"); case COLUMN_FILES_NAME: return QString("File"); - case COLUMN_FILES_DATE: return QString("Publish date"); + case COLUMN_FILES_DATE: return QString("Published"); default: return QString("[No data]"); } From 6a3fe9ebf0b73dbaee39de49f36697f37ee6b9b9 Mon Sep 17 00:00:00 2001 From: csoler Date: Fri, 19 Jun 2020 23:03:13 +0200 Subject: [PATCH 76/89] added copy link and navigate functions. --- .../GxsChannelPostsWidgetWithModel.cpp | 77 ++++++++++++++++--- .../GxsChannelPostsWidgetWithModel.h | 2 +- .../gui/gxsforums/GxsForumThreadWidget.cpp | 42 +++++----- 3 files changed, 89 insertions(+), 32 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 5d3702f9a..6d51636eb 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "retroshare/rsgxscircles.h" @@ -71,6 +72,7 @@ static const int CHANNEL_TABS_POSTS = 1; #define COLUMN_SIZE_FONT_FACTOR_H 10 #define STAR_OVERLAY_IMAGE ":icons/star_overlay_128.png" +#define IMAGE_COPYLINK ":/images/copyrslink.png" Q_DECLARE_METATYPE(ChannelPostFileInfo) @@ -326,10 +328,43 @@ void GxsChannelPostsWidgetWithModel::postContextMenu(const QPoint&) { QMenu menu(this); + menu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); + if(IS_GROUP_PUBLISHER(mGroup.mMeta.mSubscribeFlags)) - { menu.addAction(FilesDefs::getIconFromQtResourcePath(":/images/edit_16.png"), tr("Edit"), this, SLOT(editPost())); - menu.exec(QCursor::pos()); + + menu.exec(QCursor::pos()); +} + +void GxsChannelPostsWidgetWithModel::copyMessageLink() +{ + try + { + if (groupId().isNull()) + throw std::runtime_error("No channel currently selected!"); + + QModelIndex index = ui->postsTree->selectionModel()->currentIndex(); + + if(!index.isValid()) + throw std::runtime_error("No post under mouse!"); + + RsGxsChannelPost post = index.data(Qt::UserRole).value() ; + + if(post.mMeta.mMsgId.isNull()) + throw std::runtime_error("Post has empty MsgId!"); + + RetroShareLink link = RetroShareLink::createGxsMessageLink(RetroShareLink::TYPE_CHANNEL, groupId(), post.mMeta.mMsgId, QString::fromUtf8(post.mMeta.mMsgName.c_str())); + + if (!link.valid()) + throw std::runtime_error("Link is not valid"); + + QList urls; + urls.push_back(link); + RSLinkClipboard::copyLinks(urls); + } + catch(std::exception& e) + { + QMessageBox::critical(NULL,tr("Link creation error"),tr("Link could not be created: ")+e.what()); } } @@ -386,7 +421,6 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->postName_LB->hide(); ui->postTime_LB->hide(); mChannelPostFilesModel->clear(); - mSelectedGroup.clear(); mSelectedPost.clear(); return; } @@ -398,7 +432,6 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() if(index.row()==0 && index.column()==0) std::cerr << "here" << std::endl; - mSelectedGroup = mGroup.mMeta.mGroupId; mSelectedPost = post.mMeta.mMsgId; std::list files; @@ -454,7 +487,12 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() void GxsChannelPostsWidgetWithModel::updateGroupData() { if(groupId().isNull()) + { + // clear post, files and comment widgets + + showPostDetails(); return; + } RsThread::async([this]() { @@ -486,12 +524,18 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() { std::cerr << "Post channel load..." << std::endl; - if(!mSelectedPost.isNull() && mGroup.mMeta.mGroupId == mSelectedGroup) + if(!mSelectedPost.isNull()) { QModelIndex index = mChannelPostsModel->getIndexOfMessage(mSelectedPost); std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post " << mSelectedPost << std::endl; - whileBlocking(ui->postsTree)->setCurrentIndex(index); + + ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); + ui->postsTree->scrollTo(ui->postsTree->currentIndex());//May change if model reloaded + ui->postsTree->setFocus(); + ui->postsTree->update(); } + else + mSelectedPost.clear(); std::list files; @@ -978,11 +1022,24 @@ void GxsChannelPostsWidgetWithModel::blank() } -bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId &msgId) +bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId& msgId) { -#warning TODO - //return ui->feedWidget->scrollTo(feedItem, true); - return true; + QModelIndex index = mChannelPostsModel->getIndexOfMessage(msgId); + + if(!index.isValid()) + { + std::cerr << "(EE) Cannot navigate to msg " << msgId << " in channel " << mGroup.mMeta.mGroupId << ": index unknown. Setting mNavigatePendingMsgId." << std::endl; + + mSelectedPost = msgId; // not found. That means the forum may not be loaded yet. So we keep that post in mind, for after loading. + return true; // we have to return true here, otherwise the caller will intepret the async loading as an error. + } + + ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); + ui->postsTree->scrollTo(ui->postsTree->currentIndex());//May change if model reloaded + ui->postsTree->setFocus(); + ui->postsTree->update(); + + return true; } void GxsChannelPostsWidgetWithModel::subscribeGroup(bool subscribe) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index f278fcf89..86b840fd0 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -141,6 +141,7 @@ private slots: void postChannelPostLoad(); void editPost(); void postContextMenu(const QPoint&); + void copyMessageLink(); public slots: void sortColumnFiles(int col,Qt::SortOrder so); @@ -169,7 +170,6 @@ private: RsGxsChannelPostFilesModel *mChannelFilesModel; RsGxsMessageId mSelectedPost; - RsGxsGroupId mSelectedGroup; /* UI - from Designer */ Ui::GxsChannelPostsWidgetWithModel *ui; diff --git a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp index 5e7e481b3..29cfa5b44 100644 --- a/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp +++ b/retroshare-gui/src/gui/gxsforums/GxsForumThreadWidget.cpp @@ -123,9 +123,9 @@ public: { default: case 3: - case 0: icon = QIcon(IMAGE_VOID); break; - case 1: icon = QIcon(IMAGE_WARNING_YELLOW); break; - case 2: icon = QIcon(IMAGE_WARNING_RED); break; + case 0: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_VOID); break; + case 1: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_WARNING_YELLOW); break; + case 2: icon = FilesDefs::getIconFromQtResourcePath(IMAGE_WARNING_RED); break; } QPixmap pix = icon.pixmap(r.size()); @@ -172,9 +172,9 @@ public: else { if (unread) - icon = QIcon(":/images/message-state-unread.png"); + icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-unread.png"); else - icon = QIcon(":/images/message-state-read.png"); + icon = FilesDefs::getIconFromQtResourcePath(":/images/message-state-read.png"); } QPixmap pix = icon.pixmap(r.size()); @@ -462,7 +462,7 @@ QString GxsForumThreadWidget::groupName(bool withUnreadCount) QIcon GxsForumThreadWidget::groupIcon() { if (mNewCount) { - return QIcon(":/images/message-state-new.png"); + return FilesDefs::getIconFromQtResourcePath(":/images/message-state-new.png"); } return QIcon(); @@ -561,35 +561,35 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) #ifdef DEBUG_FORUMS std::cerr << "Clicked on msg " << current_post.mMsgId << std::endl; #endif - QAction *editAct = new QAction(QIcon(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu); + QAction *editAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEEDIT), tr("Edit"), &contextMnu); connect(editAct, SIGNAL(triggered()), this, SLOT(editforummessage())); bool is_pinned = mForumGroup.mPinnedPosts.ids.find(mThreadId) != mForumGroup.mPinnedPosts.ids.end(); - QAction *pinUpPostAct = new QAction(QIcon(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu); + QAction *pinUpPostAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_PINPOST), (is_pinned?tr("Un-pin this post"):tr("Pin this post up")), &contextMnu); connect(pinUpPostAct , SIGNAL(triggered()), this, SLOT(togglePinUpPost())); - QAction *replyAct = new QAction(QIcon(IMAGE_REPLY), tr("Reply"), &contextMnu); + QAction *replyAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_REPLY), tr("Reply"), &contextMnu); connect(replyAct, SIGNAL(triggered()), this, SLOT(replytoforummessage())); - QAction *replyauthorAct = new QAction(QIcon(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu); + QAction *replyauthorAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGEREPLY), tr("Reply to author with private message"), &contextMnu); connect(replyauthorAct, SIGNAL(triggered()), this, SLOT(reply_with_private_message())); - QAction *flagaspositiveAct = new QAction(QIcon(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu); + QAction *flagaspositiveAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_POSITIVE_OPINION), tr("Give positive opinion"), &contextMnu); flagaspositiveAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; flagaspositiveAct->setData(static_cast(RsOpinion::POSITIVE)); connect(flagaspositiveAct, SIGNAL(triggered()), this, SLOT(flagperson())); - QAction *flagasneutralAct = new QAction(QIcon(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu); + QAction *flagasneutralAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEUTRAL_OPINION), tr("Give neutral opinion"), &contextMnu); flagasneutralAct->setToolTip(tr("Doing this, you trust your friends to decide to forward this message or not.")) ; flagasneutralAct->setData(static_cast(RsOpinion::NEUTRAL)); connect(flagasneutralAct, SIGNAL(triggered()), this, SLOT(flagperson())); - QAction *flagasnegativeAct = new QAction(QIcon(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu); + QAction *flagasnegativeAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_NEGATIVE_OPINION), tr("Give negative opinion"), &contextMnu); flagasnegativeAct->setToolTip(tr("This will block/hide messages from this person, and notify friend nodes.")) ; flagasnegativeAct->setData(static_cast(RsOpinion::NEGATIVE)); connect(flagasnegativeAct, SIGNAL(triggered()), this, SLOT(flagperson())); - QAction *newthreadAct = new QAction(QIcon(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu); + QAction *newthreadAct = new QAction(FilesDefs::getIconFromQtResourcePath(IMAGE_MESSAGE), tr("Start New Thread"), &contextMnu); newthreadAct->setEnabled (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)); connect(newthreadAct , SIGNAL(triggered()), this, SLOT(createthread())); @@ -599,19 +599,19 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) QAction* collapseAll = new QAction(tr( "Collapse all"), &contextMnu); connect(collapseAll, SIGNAL(triggered()), ui->threadTreeWidget, SLOT(collapseAll())); - QAction *markMsgAsRead = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu); + QAction *markMsgAsRead = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read"), &contextMnu); connect(markMsgAsRead, SIGNAL(triggered()), this, SLOT(markMsgAsRead())); - QAction *markMsgAsReadChildren = new QAction(QIcon(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu); + QAction *markMsgAsReadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail-read.png"), tr("Mark as read") + " (" + tr ("with children") + ")", &contextMnu); connect(markMsgAsReadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsReadChildren())); - QAction *markMsgAsUnread = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu); + QAction *markMsgAsUnread = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread"), &contextMnu); connect(markMsgAsUnread, SIGNAL(triggered()), this, SLOT(markMsgAsUnread())); - QAction *markMsgAsUnreadChildren = new QAction(QIcon(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu); + QAction *markMsgAsUnreadChildren = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/message-mail.png"), tr("Mark as unread") + " (" + tr ("with children") + ")", &contextMnu); connect(markMsgAsUnreadChildren, SIGNAL(triggered()), this, SLOT(markMsgAsUnreadChildren())); - QAction *showinpeopleAct = new QAction(QIcon(":/images/info16.png"), tr("Show author in people tab"), &contextMnu); + QAction *showinpeopleAct = new QAction(FilesDefs::getIconFromQtResourcePath(":/images/info16.png"), tr("Show author in people tab"), &contextMnu); connect(showinpeopleAct, SIGNAL(triggered()), this, SLOT(showInPeopleTab())); if (IS_GROUP_SUBSCRIBED(mForumGroup.mMeta.mSubscribeFlags)) @@ -664,7 +664,7 @@ void GxsForumThreadWidget::threadListCustomPopupMenu(QPoint /*point*/) contextMnu.addAction(replyAct); contextMnu.addAction(newthreadAct); - QAction* action = contextMnu.addAction(QIcon(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); + QAction* action = contextMnu.addAction(FilesDefs::getIconFromQtResourcePath(IMAGE_COPYLINK), tr("Copy RetroShare Link"), this, SLOT(copyMessageLink())); action->setEnabled(!groupId().isNull() && !mThreadId.isNull()); contextMnu.addSeparator(); contextMnu.addAction(markMsgAsRead); @@ -756,7 +756,7 @@ void GxsForumThreadWidget::togglethreadview_internal() { // if (ui->expandButton->isChecked()) { ui->postText->setVisible(true); - ui->expandButton->setIcon(QIcon(QString(":/images/edit_remove24.png"))); + ui->expandButton->setIcon(FilesDefs::getIconFromQtResourcePath(QString(":/images/edit_remove24.png"))); ui->expandButton->setToolTip(tr("Hide")); // } else { // ui->postText->setVisible(false); From d0a373c14f12f4025ac9e377e5e4e127624a0980 Mon Sep 17 00:00:00 2001 From: csoler Date: Sun, 21 Jun 2020 21:43:10 +0200 Subject: [PATCH 77/89] print more debug info and added missing emit to display the channel tab name after loading --- libretroshare/src/services/p3gxscircles.cc | 2 ++ .../GxsChannelPostsWidgetWithModel.cpp | 18 ++++++++++-------- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/libretroshare/src/services/p3gxscircles.cc b/libretroshare/src/services/p3gxscircles.cc index 92b0c669e..58da554a1 100644 --- a/libretroshare/src/services/p3gxscircles.cc +++ b/libretroshare/src/services/p3gxscircles.cc @@ -1997,7 +1997,9 @@ bool p3GxsCircles::processMembershipRequests(uint32_t token) // now do another sweep and remove all msgs that are older than the latest +#ifdef DEBUG_CIRCLES std::cerr << " Cleaning old messages..." << std::endl; +#endif for(uint32_t i=0;isecond.size();++i) { diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 4a80b220e..8eabbcabf 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -417,9 +417,6 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() QTextDocument doc; doc.setHtml(post.mMsg.c_str()); - if(doc.toPlainText().trimmed().isEmpty()) - ui->postDetails_TE->setPlaceholderText(tr("No message in this post")); - if(post.mMeta.mPublishTs == 0) { ui->postDetails_TE->clear(); @@ -427,6 +424,8 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->postName_LB->hide(); ui->postTime_LB->hide(); mChannelPostFilesModel->clear(); + + std::cerr << "showPostDetails: no valid post. Clearing mSelectedPost." << std::endl; mSelectedPost.clear(); return; } @@ -438,6 +437,7 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() if(index.row()==0 && index.column()==0) std::cerr << "here" << std::endl; + std::cerr << "showPostDetails: setting mSelectedPost to current post Id " << post.mMeta.mMsgId << ". Previous value: " << mSelectedPost << std::endl; mSelectedPost = post.mMeta.mMsgId; std::list files; @@ -522,6 +522,8 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() mChannelPostsModel->updateChannel(groupId()); insertChannelDetails(mGroup); + + emit groupChanged(this); // signals the parent widget to e.g. update the group tab name } ); }); } @@ -533,7 +535,9 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() if(!mSelectedPost.isNull()) { QModelIndex index = mChannelPostsModel->getIndexOfMessage(mSelectedPost); - std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post " << mSelectedPost << std::endl; + + std::cerr << "Setting current index to " << index.row() << ","<< index.column() << " for current post " + << mSelectedPost.toStdString() << std::endl; ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); ui->postsTree->scrollTo(ui->postsTree->currentIndex());//May change if model reloaded @@ -541,7 +545,7 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() ui->postsTree->update(); } else - mSelectedPost.clear(); + std::cerr << "No pre-selected channel post." << std::endl; std::list files; @@ -551,7 +555,6 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() //ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_FILE); //ui->channelFiles_TV->resizeColumnToContents(RsGxsChannelPostFilesModel::COLUMN_FILES_SIZE); ui->channelFiles_TV->setAutoSelect(true); - ui->postDetails_TE->setPlaceholderText(tr("No post selected")); } @@ -651,7 +654,7 @@ void GxsChannelPostsWidgetWithModel::settingsChanged() QString GxsChannelPostsWidgetWithModel::groupName(bool) { - return "Group name" ; + return QString::fromUtf8(mGroup.mMeta.mGroupName.c_str()); } void GxsChannelPostsWidgetWithModel::groupNameChanged(const QString &name) @@ -1026,7 +1029,6 @@ void GxsChannelPostsWidgetWithModel::blank() mChannelPostsModel->clear(); mChannelPostFilesModel->clear(); ui->postDetails_TE->clear(); - ui->postDetails_TE->setPlaceholderText(tr("No post selected")); ui->postLogo_LB->hide(); ui->postName_LB->hide(); ui->postTime_LB->hide(); From 6863f4cc7033c0b7a44d6fba5df4b7e76e45e801 Mon Sep 17 00:00:00 2001 From: hunbernd Date: Sun, 21 Jun 2020 21:55:52 +0200 Subject: [PATCH 78/89] Fix hidden node crashing at the startup, caused by calling uninitialized objects. --- libretroshare/src/pqi/p3netmgr.cc | 10 ++++++---- libretroshare/src/pqi/p3netmgr.h | 4 ++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/libretroshare/src/pqi/p3netmgr.cc b/libretroshare/src/pqi/p3netmgr.cc index 38007c309..cf4ab36fc 100644 --- a/libretroshare/src/pqi/p3netmgr.cc +++ b/libretroshare/src/pqi/p3netmgr.cc @@ -1803,15 +1803,16 @@ void p3NetMgrIMPL::updateNatSetting() #endif #ifdef RS_USE_DHT_STUNNER - switch(natType) - { - case RSNET_NATTYPE_RESTRICTED_CONE: + if (mProxyStunner) { + switch(natType) + { + case RSNET_NATTYPE_RESTRICTED_CONE: { if ((natHole == RSNET_NATHOLE_NONE) || (natHole == RSNET_NATHOLE_UNKNOWN)) { mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_FAST); } - else + else { mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_SLOW); } @@ -1826,6 +1827,7 @@ void p3NetMgrIMPL::updateNatSetting() mProxyStunner->setRefreshPeriod(NET_STUNNER_PERIOD_SLOW); break; + } } #endif // RS_USE_DHT_STUNNER diff --git a/libretroshare/src/pqi/p3netmgr.h b/libretroshare/src/pqi/p3netmgr.h index f7775757d..05c9b743a 100644 --- a/libretroshare/src/pqi/p3netmgr.h +++ b/libretroshare/src/pqi/p3netmgr.h @@ -291,8 +291,8 @@ private: //p3BitDht *mBitDht; #ifdef RS_USE_DHT_STUNNER - pqiAddrAssist *mDhtStunner; - pqiAddrAssist *mProxyStunner; + pqiAddrAssist *mDhtStunner = nullptr; + pqiAddrAssist *mProxyStunner = nullptr; #endif // RS_USE_DHT_STUNNER RsMutex mNetMtx; /* protects below */ From 1c2e094f20e1c8740b2fd7f14daa07ac4c5f1fa7 Mon Sep 17 00:00:00 2001 From: csoler Date: Mon, 22 Jun 2020 23:52:59 +0200 Subject: [PATCH 79/89] removed tab system temporarily until we fix it --- retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index 1245c80b2..ed00e16d1 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -432,12 +432,14 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) QMenu contextMnu(this); QAction *action; +#ifdef TODO if (mMessageWidget) { action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); if (mGroupId.isNull() || messageWidget(mGroupId, true)) { action->setEnabled(false); } } +#endif if (isSubscribed) { action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup())); @@ -817,7 +819,7 @@ GxsCommentDialog *GxsGroupFrameDialog::commentWidget(const RsGxsMessageId& msgId return NULL; } -void GxsGroupFrameDialog::changedCurrentGroup(const QString &groupId) +void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId) { if (mInFill) { return; @@ -839,8 +841,10 @@ void GxsGroupFrameDialog::changedCurrentGroup(const QString &groupId) /* search exisiting tab */ GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, true); - if (!msgWidget) { - if (mMessageWidget) { + if (!msgWidget) + { + if (mMessageWidget) + { /* not found, use standard tab */ msgWidget = mMessageWidget; msgWidget->setGroupId(mGroupId); From 97ad766863108ec390e01dcb1fda6b3790ebb9ab Mon Sep 17 00:00:00 2001 From: csoler Date: Tue, 23 Jun 2020 00:44:09 +0200 Subject: [PATCH 80/89] fixed selection of current item from link in channels --- .../src/gui/gxschannels/GxsChannelPostsModel.cpp | 4 ++-- .../gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 8 ++------ 2 files changed, 4 insertions(+), 8 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp index 8a78a59ca..47f46bfe9 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsModel.cpp @@ -719,7 +719,7 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) quintptr ref ; convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab - return createIndex(i%mColumns, i/mColumns,ref); + return createIndex(i/mColumns,i%mColumns, ref); } if(mPosts[mFilteredPosts[i]].mMeta.mMsgId == postId) @@ -727,7 +727,7 @@ QModelIndex RsGxsChannelPostsModel::getIndexOfMessage(const RsGxsMessageId& mid) quintptr ref ; convertTabEntryToRefPointer(i,ref); // we dont use i+1 here because i is not a row, but an index in the mPosts tab - return createIndex(i%mColumns, i/mColumns,ref); + return createIndex(i/mColumns,i%mColumns, ref); } } diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 8eabbcabf..7322841cc 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -425,8 +425,6 @@ void GxsChannelPostsWidgetWithModel::showPostDetails() ui->postTime_LB->hide(); mChannelPostFilesModel->clear(); - std::cerr << "showPostDetails: no valid post. Clearing mSelectedPost." << std::endl; - mSelectedPost.clear(); return; } @@ -540,9 +538,8 @@ void GxsChannelPostsWidgetWithModel::postChannelPostLoad() << mSelectedPost.toStdString() << std::endl; ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); - ui->postsTree->scrollTo(ui->postsTree->currentIndex());//May change if model reloaded + ui->postsTree->scrollTo(index);//May change if model reloaded ui->postsTree->setFocus(); - ui->postsTree->update(); } else std::cerr << "No pre-selected channel post." << std::endl; @@ -1049,9 +1046,8 @@ bool GxsChannelPostsWidgetWithModel::navigate(const RsGxsMessageId& msgId) } ui->postsTree->selectionModel()->setCurrentIndex(index,QItemSelectionModel::ClearAndSelect); - ui->postsTree->scrollTo(ui->postsTree->currentIndex());//May change if model reloaded + ui->postsTree->scrollTo(index);//May change if model reloaded ui->postsTree->setFocus(); - ui->postsTree->update(); return true; } From d39b09c5bcb2aafe95adaf67f6730050cccbd93a Mon Sep 17 00:00:00 2001 From: csoler Date: Tue, 23 Jun 2020 20:24:14 +0200 Subject: [PATCH 81/89] added zoom of thumbnails on control+wheel --- retroshare-gui/src/gui/common/RSTreeView.cpp | 8 +++ retroshare-gui/src/gui/common/RSTreeView.h | 2 + .../src/gui/gxs/GxsGroupFrameDialog.cpp | 49 ++++++++++--------- .../src/gui/gxs/GxsGroupFrameDialog.h | 5 +- .../src/gui/gxs/GxsMessageFrameWidget.h | 1 + .../GxsChannelPostsWidgetWithModel.cpp | 47 ++++++++++++++++-- .../GxsChannelPostsWidgetWithModel.h | 8 ++- .../GxsChannelPostsWidgetWithModel.ui | 4 +- 8 files changed, 91 insertions(+), 33 deletions(-) diff --git a/retroshare-gui/src/gui/common/RSTreeView.cpp b/retroshare-gui/src/gui/common/RSTreeView.cpp index 36034166f..d54987c3e 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.cpp +++ b/retroshare-gui/src/gui/common/RSTreeView.cpp @@ -27,6 +27,14 @@ RSTreeView::RSTreeView(QWidget *parent) : QTreeView(parent) setMouseTracking(false); // normally the default, but who knows if it's not goign to change in the future. } +void RSTreeView::wheelEvent(QWheelEvent *e) +{ + if(e->modifiers() == Qt::ControlModifier) + emit zoomRequested(e->delta() > 0); + else + QTreeView::wheelEvent(e); +} + void RSTreeView::mouseMoveEvent(QMouseEvent *e) { QModelIndex idx = indexAt(e->pos()); diff --git a/retroshare-gui/src/gui/common/RSTreeView.h b/retroshare-gui/src/gui/common/RSTreeView.h index f7942ef64..116ccac31 100644 --- a/retroshare-gui/src/gui/common/RSTreeView.h +++ b/retroshare-gui/src/gui/common/RSTreeView.h @@ -40,9 +40,11 @@ public: signals: void sizeChanged(QSize); + void zoomRequested(bool zoom_or_unzoom); protected: virtual void mouseMoveEvent(QMouseEvent *e) override; // overriding so as to manage auto-selection + virtual void wheelEvent(QWheelEvent *e) override; // overriding so as to manage zoom virtual void resizeEvent(QResizeEvent *e) override; virtual void paintEvent(QPaintEvent *event) override; diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index ed00e16d1..a2be34e8b 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -390,6 +390,7 @@ static uint32_t checkDelay(uint32_t time_in_secs) return 365 * 86400; } + void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) { // First separately handle the case of search top level items @@ -433,11 +434,12 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) QAction *action; #ifdef TODO - if (mMessageWidget) { - action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); - if (mGroupId.isNull() || messageWidget(mGroupId, true)) { + if (mMessageWidget) + { + action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open new tab"), this, SLOT(openInNewTab())); + + if (mGroupId.isNull() || messageWidget(mGroupId, true)) // dont enable the open in tab if a tab is already here action->setEnabled(false); - } } #endif @@ -841,20 +843,19 @@ void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId) /* search exisiting tab */ GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, true); - if (!msgWidget) + // check that we have at least one tab + if(!currentWidget()) + msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString())); + + if (msgWidget) + ui->messageTabWidget->setCurrentWidget(msgWidget); + else { - if (mMessageWidget) - { - /* not found, use standard tab */ - msgWidget = mMessageWidget; - msgWidget->setGroupId(mGroupId); - } else { - /* create new tab */ - msgWidget = createMessageWidget(mGroupId); - } + msgWidget = currentWidget(); + msgWidget->setGroupId(mGroupId); } - ui->messageTabWidget->setCurrentWidget(msgWidget); + mMessageWidget = msgWidget; } void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item) @@ -885,18 +886,18 @@ void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId) void GxsGroupFrameDialog::messageTabCloseRequested(int index) { - QWidget *widget = ui->messageTabWidget->widget(index); - if (!widget) { + if(ui->messageTabWidget->count() == 1) /* Don't close single tab */ return; - } - GxsMessageFrameWidget *msgWidget = dynamic_cast(widget); - if (msgWidget && msgWidget == mMessageWidget) { - /* Don't close single tab */ - return; - } + GxsMessageFrameWidget *msgWidget = dynamic_cast(ui->messageTabWidget->widget(index)); + delete msgWidget ; - delete(widget); + mMessageWidget = currentWidget(); +} + +GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const +{ + return dynamic_cast(ui->messageTabWidget->widget(ui->messageTabWidget->currentIndex())); } void GxsGroupFrameDialog::messageTabChanged(int index) diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h index 902e49087..e1d088a4b 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h @@ -192,13 +192,16 @@ protected: bool mCountChildMsgs; // Count unread child messages? private: + GxsMessageFrameWidget *currentWidget() const; + bool mInitialized; bool mInFill; bool mDistSyncAllowed; QString mSettingsName; RsGxsGroupId mGroupId; RsGxsIfaceHelper *mInterface; - GxsMessageFrameWidget *mMessageWidget; + + GxsMessageFrameWidget *mMessageWidget; // current widget QTreeWidgetItem *mYourGroups; QTreeWidgetItem *mSubscribedGroups; diff --git a/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h b/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h index 9b9a8e34e..36c2f4f67 100644 --- a/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h +++ b/retroshare-gui/src/gui/gxs/GxsMessageFrameWidget.h @@ -55,6 +55,7 @@ signals: void groupChanged(QWidget *widget); void waitingChanged(QWidget *widget); void loadComment(const RsGxsGroupId &groupId, const QVector& msg_versions,const RsGxsMessageId &msgId, const QString &title); + void groupDataLoaded(); protected: virtual void setAllMessagesReadDo(bool read, uint32_t &token) = 0; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 7322841cc..5c9c70437 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -78,6 +78,21 @@ Q_DECLARE_METATYPE(ChannelPostFileInfo) // Delegate used to paint into the table of thumbnails +int ChannelPostDelegate::cellSize(const QFont& font) const +{ + return mZoom*COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font).height(); +} + +void ChannelPostDelegate::zoom(bool zoom_or_unzoom) +{ + if(zoom_or_unzoom) + mZoom *= 1.02; + else + mZoom /= 1.02; + + std::cerr << "zoom factor: " << mZoom << std::endl; +} + void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const { // prepare @@ -101,11 +116,14 @@ void ChannelPostDelegate::paint(QPainter * painter, const QStyleOptionViewItem & w.render(&pixmap,QPoint(),QRegion(),QWidget::DrawChildren );// draw the widgets, not the background + if(mZoom != 1.0) + pixmap = pixmap.scaled(mZoom*pixmap.size(),Qt::KeepAspectRatio,Qt::SmoothTransformation); + if(IS_MSG_UNREAD(post.mMeta.mMsgStatus) || IS_MSG_NEW(post.mMeta.mMsgStatus)) { QPainter p(&pixmap); QFontMetricsF fm(option.font); - p.drawPixmap(QPoint(6.2*fm.height(),6.9*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(7*fm.height(),7*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation)); + p.drawPixmap(mZoom*QPoint(6.2*fm.height(),6.9*fm.height()),FilesDefs::getPixmapFromQtResourcePath(STAR_OVERLAY_IMAGE).scaled(mZoom*7*fm.height(),mZoom*7*fm.height(),Qt::KeepAspectRatio,Qt::SmoothTransformation)); } // debug @@ -128,7 +146,7 @@ QSize ChannelPostDelegate::sizeHint(const QStyleOptionViewItem& option, const QM QFontMetricsF fm(option.font); - return QSize(COLUMN_SIZE_FONT_FACTOR_W*fm.height(),COLUMN_SIZE_FONT_FACTOR_H*fm.height()); + return QSize(mZoom*COLUMN_SIZE_FONT_FACTOR_W*fm.height(),mZoom*COLUMN_SIZE_FONT_FACTOR_H*fm.height()); } QWidget *ChannelPostFilesDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex& index) const @@ -213,11 +231,13 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI ui->setupUi(this); ui->postsTree->setModel(mChannelPostsModel = new RsGxsChannelPostsModel()); - ui->postsTree->setItemDelegate(new ChannelPostDelegate()); + ui->postsTree->setItemDelegate(mChannelPostsDelegate = new ChannelPostDelegate()); ui->postsTree->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff); // prevents bug on w10, since row size depends on widget width ui->postsTree->setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);// more beautiful if we scroll at pixel level ui->postsTree->setVerticalScrollMode(QAbstractItemView::ScrollPerPixel); + connect(ui->postsTree,SIGNAL(zoomRequested(bool)),this,SLOT(updateZoomFactor(bool))); + ui->channelPostFiles_TV->setModel(mChannelPostFilesModel = new RsGxsChannelPostFilesModel(this)); ui->channelPostFiles_TV->setItemDelegate(new ChannelPostFilesDelegate()); ui->channelPostFiles_TV->setPlaceholderText(tr("No files in this post, or no post selected")); @@ -251,7 +271,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI QFontMetricsF fm(font()); for(int i=0;icolumnCount();++i) - ui->postsTree->setColumnWidth(i,COLUMN_SIZE_FONT_FACTOR_W*fm.height()); + ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(font())); /* Setup UI helper */ @@ -313,6 +333,23 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI }, mEventHandlerId, RsEventType::GXS_CHANNELS ); } +void GxsChannelPostsWidgetWithModel::updateZoomFactor(bool zoom_or_unzoom) +{ + mChannelPostsDelegate->zoom(zoom_or_unzoom); + + for(int i=0;icolumnCount();++i) + ui->postsTree->setColumnWidth(i,mChannelPostsDelegate->cellSize(font())); + + QSize s = ui->postsTree->size(); + + int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(font())))); + std::cerr << "nb columns: " << n_columns << std::endl; + + mChannelPostsModel->setNumColumns(n_columns); // forces the update + + ui->postsTree->dataChanged(QModelIndex(),QModelIndex()); +} + void GxsChannelPostsWidgetWithModel::sortColumnPostFiles(int col,Qt::SortOrder so) { std::cerr << "Sorting post files according to col " << col << std::endl; @@ -379,7 +416,7 @@ void GxsChannelPostsWidgetWithModel::editPost() void GxsChannelPostsWidgetWithModel::handlePostsTreeSizeChange(QSize s) { - int n_columns = std::max(1,(int)floor(s.width() / (COLUMN_SIZE_FONT_FACTOR_W*QFontMetricsF(font()).height()))); + int n_columns = std::max(1,(int)floor(s.width() / (mChannelPostsDelegate->cellSize(font())))); std::cerr << "nb columns: " << n_columns << std::endl; if(n_columns != mChannelPostsModel->columnCount()) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h index 86b840fd0..be2ccdf6c 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.h @@ -60,17 +60,21 @@ class ChannelPostDelegate: public QAbstractItemDelegate Q_OBJECT public: - ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent){} + ChannelPostDelegate(QObject *parent=0) : QAbstractItemDelegate(parent), mZoom(1.0){} virtual ~ChannelPostDelegate(){} void paint(QPainter * painter, const QStyleOptionViewItem & option, const QModelIndex & index) const override; QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override; + int cellSize(const QFont& font) const; + void zoom(bool zoom_or_unzoom) ; private: static constexpr float IMAGE_MARGIN_FACTOR = 1.0; static constexpr float IMAGE_SIZE_FACTOR_W = 4.0 ; static constexpr float IMAGE_SIZE_FACTOR_H = 6.0 ; static constexpr float IMAGE_ZOOM_FACTOR = 1.0; + + float mZoom; // zoom factor for the whole thumbnail }; class GxsChannelPostsWidgetWithModel: public GxsMessageFrameWidget @@ -142,6 +146,7 @@ private slots: void editPost(); void postContextMenu(const QPoint&); void copyMessageLink(); + void updateZoomFactor(bool zoom_or_unzoom); public slots: void sortColumnFiles(int col,Qt::SortOrder so); @@ -168,6 +173,7 @@ private: RsGxsChannelPostsModel *mChannelPostsModel; RsGxsChannelPostFilesModel *mChannelPostFilesModel; RsGxsChannelPostFilesModel *mChannelFilesModel; + ChannelPostDelegate *mChannelPostsDelegate; RsGxsMessageId mSelectedPost; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui index 28687b2df..1f51702a2 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.ui @@ -161,7 +161,7 @@ - 0 + 1 @@ -337,7 +337,7 @@ <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd"> <html><head><meta name="qrichtext" content="1" /><style type="text/css"> p, li { white-space: pre-wrap; } -</style></head><body style=" font-family:'MS Shell Dlg 2'; font-size:8.25pt; font-weight:400; font-style:normal;"> +</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> From b21193e460dc2fb34836daff007be9c49950d8cf Mon Sep 17 00:00:00 2001 From: csoler Date: Tue, 23 Jun 2020 21:37:36 +0200 Subject: [PATCH 82/89] tried to improve the logic in tab system. Not finished yet --- .../src/gui/gxs/GxsGroupFrameDialog.cpp | 108 +++++++++--------- .../src/gui/gxs/GxsGroupFrameDialog.h | 5 +- .../GxsChannelPostsWidgetWithModel.cpp | 1 + 3 files changed, 56 insertions(+), 58 deletions(-) diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index a2be34e8b..890d353d9 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -89,7 +89,6 @@ GxsGroupFrameDialog::GxsGroupFrameDialog(RsGxsIfaceHelper *ifaceImpl, QWidget *p mSubscribedGroups = NULL; mPopularGroups = NULL; mOtherGroups = NULL; - mMessageWidget = NULL; /* Setup Queue */ mInterface = ifaceImpl; @@ -251,6 +250,13 @@ void GxsGroupFrameDialog::processSettings(bool load) Settings->endGroup(); } +bool GxsGroupFrameDialog::useTabs() +{ + GroupFrameSettings groupFrameSettings; + + return Settings->getGroupFrameSettings(groupFrameSettingsType(), groupFrameSettings) && groupFrameSettings.mOpenAllInNewTab; +} + void GxsGroupFrameDialog::settingsChanged() { GroupFrameSettings groupFrameSettings; @@ -262,17 +268,15 @@ void GxsGroupFrameDialog::settingsChanged() void GxsGroupFrameDialog::setSingleTab(bool singleTab) { - if (singleTab) { - if (!mMessageWidget) { - mMessageWidget = createMessageWidget(RsGxsGroupId()); - // remove close button of the the first tab - ui->messageTabWidget->hideCloseButton(ui->messageTabWidget->indexOf(mMessageWidget)); - } - } else { - if (mMessageWidget) { - delete(mMessageWidget); - mMessageWidget = NULL; - } + if (singleTab) + { + while(ui->messageTabWidget->count() > 1) + { + auto w = ui->messageTabWidget->widget(0) ; + ui->messageTabWidget->removeTab(0); + delete w; + } + ui->messageTabWidget->hideCloseButton(0); } } @@ -433,15 +437,10 @@ void GxsGroupFrameDialog::groupTreeCustomPopupMenu(QPoint point) QMenu contextMnu(this); QAction *action; -#ifdef TODO - if (mMessageWidget) - { - action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open new tab"), this, SLOT(openInNewTab())); + action = contextMnu.addAction(QIcon(IMAGE_TABNEW), tr("Open in new tab"), this, SLOT(openInNewTab())); - if (mGroupId.isNull() || messageWidget(mGroupId, true)) // dont enable the open in tab if a tab is already here - action->setEnabled(false); - } -#endif + if(mGroupId.isNull()) // dont enable the open in tab if a tab is already here + action->setEnabled(false); if (isSubscribed) { action = contextMnu.addAction(QIcon(IMAGE_UNSUBSCRIBE), tr("Unsubscribe"), this, SLOT(unsubscribeGroup())); @@ -677,7 +676,7 @@ bool GxsGroupFrameDialog::getCurrentGroupName(QString& name) void GxsGroupFrameDialog::markMsgAsRead() { - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); if (msgWidget) { msgWidget->setAllMessagesRead(true); } @@ -685,7 +684,7 @@ void GxsGroupFrameDialog::markMsgAsRead() void GxsGroupFrameDialog::markMsgAsUnread() { - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); if (msgWidget) { msgWidget->setAllMessagesRead(false); } @@ -763,7 +762,7 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa changedCurrentGroup(groupIdString); /* search exisiting tab */ - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, false); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); if (!msgWidget) { return false; } @@ -775,17 +774,16 @@ bool GxsGroupFrameDialog::navigate(const RsGxsGroupId &groupId, const RsGxsMessa return msgWidget->navigate(msgId); } -GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId, bool ownTab) +GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &groupId) { int tabCount = ui->messageTabWidget->count(); - for (int index = 0; index < tabCount; ++index) { + + for (int index = 0; index < tabCount; ++index) + { GxsMessageFrameWidget *childWidget = dynamic_cast(ui->messageTabWidget->widget(index)); - if (ownTab && mMessageWidget && childWidget == mMessageWidget) { - continue; - } - if (childWidget && childWidget->groupId() == groupId) { + + if (childWidget && childWidget->groupId() == groupId) return childWidget; - } } return NULL; @@ -794,9 +792,9 @@ GxsMessageFrameWidget *GxsGroupFrameDialog::messageWidget(const RsGxsGroupId &gr GxsMessageFrameWidget *GxsGroupFrameDialog::createMessageWidget(const RsGxsGroupId &groupId) { GxsMessageFrameWidget *msgWidget = createMessageFrameWidget(groupId); - if (!msgWidget) { + + if (!msgWidget) return NULL; - } int index = ui->messageTabWidget->addTab(msgWidget, msgWidget->groupName(true)); ui->messageTabWidget->setTabIcon(index, msgWidget->groupIcon()); @@ -827,35 +825,38 @@ void GxsGroupFrameDialog::changedCurrentGroup(const QString& groupId) return; } - if (groupId.isEmpty()) { - if (mMessageWidget) { - mMessageWidget->setGroupId(RsGxsGroupId()); - ui->messageTabWidget->setCurrentWidget(mMessageWidget); - } + if (groupId.isEmpty()) + { + auto w = currentWidget(); + + if(w) + w->setGroupId(RsGxsGroupId()); + return; } mGroupId = RsGxsGroupId(groupId.toStdString()); - if (mGroupId.isNull()) { + + if (mGroupId.isNull()) return; - } /* search exisiting tab */ - GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId, true); + GxsMessageFrameWidget *msgWidget = messageWidget(mGroupId); // check that we have at least one tab - if(!currentWidget()) - msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString())); - if (msgWidget) + if(msgWidget) ui->messageTabWidget->setCurrentWidget(msgWidget); else { - msgWidget = currentWidget(); - msgWidget->setGroupId(mGroupId); + if(useTabs() || ui->messageTabWidget->count()==0) + { + msgWidget = createMessageWidget(RsGxsGroupId(groupId.toStdString())); + ui->messageTabWidget->setCurrentWidget(msgWidget); + } + else + currentWidget()->setGroupId(mGroupId); } - - mMessageWidget = msgWidget; } void GxsGroupFrameDialog::groupTreeMiddleButtonClicked(QTreeWidgetItem *item) @@ -875,11 +876,10 @@ void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId) } /* search exisiting tab */ - GxsMessageFrameWidget *msgWidget = messageWidget(groupId, true); - if (!msgWidget) { - /* not found, create new tab */ + GxsMessageFrameWidget *msgWidget = messageWidget(groupId); + + if (!msgWidget) /* not found, create new tab */ msgWidget = createMessageWidget(groupId); - } ui->messageTabWidget->setCurrentWidget(msgWidget); } @@ -891,8 +891,6 @@ void GxsGroupFrameDialog::messageTabCloseRequested(int index) GxsMessageFrameWidget *msgWidget = dynamic_cast(ui->messageTabWidget->widget(index)); delete msgWidget ; - - mMessageWidget = currentWidget(); } GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const @@ -903,9 +901,9 @@ GxsMessageFrameWidget *GxsGroupFrameDialog::currentWidget() const void GxsGroupFrameDialog::messageTabChanged(int index) { GxsMessageFrameWidget *msgWidget = dynamic_cast(ui->messageTabWidget->widget(index)); - if (!msgWidget) { + + if (!msgWidget) return; - } ui->groupTreeWidget->activateId(QString::fromStdString(msgWidget->groupId().toStdString()), false); } diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h index e1d088a4b..73938584e 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.h @@ -181,7 +181,7 @@ private: // subscribe/unsubscribe ack. - GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId, bool ownTab); + GxsMessageFrameWidget *messageWidget(const RsGxsGroupId &groupId); GxsMessageFrameWidget *createMessageWidget(const RsGxsGroupId &groupId); GxsCommentDialog *commentWidget(const RsGxsMessageId &msgId); @@ -193,6 +193,7 @@ protected: private: GxsMessageFrameWidget *currentWidget() const; + bool useTabs(); bool mInitialized; bool mInFill; @@ -201,8 +202,6 @@ private: RsGxsGroupId mGroupId; RsGxsIfaceHelper *mInterface; - GxsMessageFrameWidget *mMessageWidget; // current widget - QTreeWidgetItem *mYourGroups; QTreeWidgetItem *mSubscribedGroups; QTreeWidgetItem *mPopularGroups; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 5c9c70437..690563321 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -558,6 +558,7 @@ void GxsChannelPostsWidgetWithModel::updateGroupData() insertChannelDetails(mGroup); + emit groupDataLoaded(); emit groupChanged(this); // signals the parent widget to e.g. update the group tab name } ); }); From c23d994bfcf55e9c30f5edaccf0ae3a300fce570 Mon Sep 17 00:00:00 2001 From: csoler Date: Tue, 23 Jun 2020 21:53:03 +0200 Subject: [PATCH 83/89] fixed update of channel data on tabs --- .../src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 690563321..6db53d2b6 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -323,6 +323,7 @@ GxsChannelPostsWidgetWithModel::GxsChannelPostsWidgetWithModel(const RsGxsGroupI setAutoDownload(false); settingsChanged(); + setGroupId(channelId); mChannelPostsModel->updateChannel(channelId); mEventHandlerId = 0; From 0eb8edf824fca94deb9fb4d0e68c88919b883a94 Mon Sep 17 00:00:00 2001 From: csoler Date: Tue, 23 Jun 2020 22:00:14 +0200 Subject: [PATCH 84/89] fixed open in new tab system --- retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp index 890d353d9..de528d23e 100644 --- a/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp +++ b/retroshare-gui/src/gui/gxs/GxsGroupFrameDialog.cpp @@ -876,10 +876,7 @@ void GxsGroupFrameDialog::openGroupInNewTab(const RsGxsGroupId &groupId) } /* search exisiting tab */ - GxsMessageFrameWidget *msgWidget = messageWidget(groupId); - - if (!msgWidget) /* not found, create new tab */ - msgWidget = createMessageWidget(groupId); + GxsMessageFrameWidget *msgWidget = createMessageWidget(groupId); ui->messageTabWidget->setCurrentWidget(msgWidget); } From d2b9b9f0949d66bd1f245ed08ac83fcf956cb9c1 Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Tue, 23 Jun 2020 23:38:22 +0200 Subject: [PATCH 85/89] Reduce memory usage due to copying in IdDialog::updateCircles Heaptrack reported 141MB of RAM where used by this method most proably due to the group metadata list being copied accross lambdas and threads more then necessary. Use an unique_ptr to safely avoid copying of big structure around. --- retroshare-gui/src/gui/Identity/IdDialog.cpp | 19 +++++++++++++------ retroshare-gui/src/util/qtthreadsutils.h | 2 +- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 1a585d532..2c81d2b10 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -47,6 +47,7 @@ #include "util/misc.h" #include "util/QtVersion.h" #include "util/rstime.h" +#include "util/rsdebug.h" #include "retroshare/rsgxsflags.h" #include "retroshare/rsmsgs.h" @@ -55,6 +56,7 @@ #include #include +#include /****** * #define ID_DEBUG 1 @@ -506,21 +508,26 @@ void IdDialog::updateCircles() std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif - std::list circle_metas ; + /* This can be big so use a smart pointer to just copy the pointer + * instead of copying the whole list accross the lambdas. + * Heaptrack reported 141MB of RAM where used to copy this around + * before this change. */ + auto circle_metas = std::make_unique>(); - if(!rsGxsCircles->getCirclesSummaries(circle_metas)) + if(!rsGxsCircles->getCirclesSummaries(*circle_metas)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve circles group info list" << std::endl; + RS_ERR("failed to retrieve circles group info list"); return; - } + } - RsQThreadUtils::postToObject( [circle_metas,this]() + RsQThreadUtils::postToObject( + [circle_metas = std::move(circle_metas), this]() { /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ - loadCircles(circle_metas); + loadCircles(*circle_metas); }, this ); diff --git a/retroshare-gui/src/util/qtthreadsutils.h b/retroshare-gui/src/util/qtthreadsutils.h index 155b71b7e..689161fde 100644 --- a/retroshare-gui/src/util/qtthreadsutils.h +++ b/retroshare-gui/src/util/qtthreadsutils.h @@ -44,7 +44,7 @@ void postToObject(F &&fun, QObject *obj = qApp) QObject src; auto type = obj->metaObject(); QObject::connect( &src, &QObject::destroyed, obj, - [fun, type, obj] + [fun = std::move(fun), type, obj] { // ensure that the object is not being destructed if (obj->metaObject()->inherits(type)) fun(); From 3b37c1e9ad2e2c2919f9f66cbb4e101d5ac948a6 Mon Sep 17 00:00:00 2001 From: Gioacchino Mazzurco Date: Wed, 24 Jun 2020 00:21:33 +0200 Subject: [PATCH 86/89] Remove more copying of big structures in RsQThreadUtils::postToObject --- .../src/gui/Circles/CreateCircleDialog.cpp | 27 +++++++++---------- retroshare-gui/src/gui/Identity/IdDialog.cpp | 18 +++++-------- .../src/gui/Identity/IdEditDialog.cpp | 4 +-- .../src/gui/common/FriendSelectionWidget.cpp | 26 +++++++++--------- .../gui/statistics/GxsTransportStatistics.cpp | 24 +++++++++-------- retroshare-gui/src/util/qtthreadsutils.h | 4 ++- 6 files changed, 50 insertions(+), 53 deletions(-) diff --git a/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp b/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp index 59b0da95d..3733c0f1a 100644 --- a/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp +++ b/retroshare-gui/src/gui/Circles/CreateCircleDialog.cpp @@ -24,6 +24,7 @@ #include #include +#include #include #include @@ -696,34 +697,32 @@ void CreateCircleDialog::loadIdentities() { RsThread::async([this]() { - std::list ids_meta; + std::list ids_meta; if(!rsIdentity->getIdentitiesSummaries(ids_meta)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities ids for all identities" << std::endl; + RS_ERR("failed to retrieve identities ids for all identities"); return; - } - std::set ids; + } - for(auto& meta:ids_meta) - ids.insert(RsGxsId(meta.mGroupId)) ; + std::set ids; + for(auto& meta:ids_meta) ids.insert(RsGxsId(meta.mGroupId)); - std::vector id_groups; - - if(!rsIdentity->getIdentitiesInfo(ids,id_groups)) + auto id_groups = std::make_unique>(); + if(!rsIdentity->getIdentitiesInfo(ids, *id_groups)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for all identities" << std::endl; + RS_ERR("failed to retrieve identities group info for all identities"); return; - } + } - RsQThreadUtils::postToObject( [id_groups,this]() + RsQThreadUtils::postToObject( + [id_groups = std::move(id_groups), this]() { /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ - fillIdentitiesList(id_groups) ; - + fillIdentitiesList(*id_groups); }, this ); }); diff --git a/retroshare-gui/src/gui/Identity/IdDialog.cpp b/retroshare-gui/src/gui/Identity/IdDialog.cpp index 2c81d2b10..d49e558ec 100644 --- a/retroshare-gui/src/gui/Identity/IdDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdDialog.cpp @@ -509,9 +509,7 @@ void IdDialog::updateCircles() #endif /* This can be big so use a smart pointer to just copy the pointer - * instead of copying the whole list accross the lambdas. - * Heaptrack reported 141MB of RAM where used to copy this around - * before this change. */ + * instead of copying the whole list accross the lambdas */ auto circle_metas = std::make_unique>(); if(!rsGxsCircles->getCirclesSummaries(*circle_metas)) @@ -1312,19 +1310,17 @@ void IdDialog::updateIdList() return; } - std::map ids_set; + auto ids_set = std::make_unique>(); + for(auto it(groups.begin()); it!=groups.end(); ++it) + (*ids_set)[(*it).mMeta.mGroupId] = *it; - for(auto it(groups.begin());it!=groups.end();++it) - ids_set[(*it).mMeta.mGroupId] = *it; - - RsQThreadUtils::postToObject( [ids_set,this]() + RsQThreadUtils::postToObject( + [ids_set = std::move(ids_set), this] () { /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ - - loadIdentities(ids_set); - + loadIdentities(*ids_set); }, this ); }); diff --git a/retroshare-gui/src/gui/Identity/IdEditDialog.cpp b/retroshare-gui/src/gui/Identity/IdEditDialog.cpp index bb1ee139a..0f432ba2d 100644 --- a/retroshare-gui/src/gui/Identity/IdEditDialog.cpp +++ b/retroshare-gui/src/gui/Identity/IdEditDialog.cpp @@ -210,8 +210,8 @@ void IdEditDialog::setupExistingId(const RsGxsGroupId& keyId) RsThread::async([this,keyId]() { std::vector datavector; - - bool res = rsIdentity->getIdentitiesInfo(std::set({(RsGxsId)keyId}),datavector); + bool res = rsIdentity->getIdentitiesInfo( + std::set({(RsGxsId)keyId}), datavector ); RsQThreadUtils::postToObject( [this,keyId,res,datavector]() { diff --git a/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp b/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp index fa5c439f1..4043fe424 100644 --- a/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp +++ b/retroshare-gui/src/gui/common/FriendSelectionWidget.cpp @@ -36,6 +36,7 @@ #include #include +#include #define COLUMN_NAME 0 #define COLUMN_CHECK 0 @@ -250,24 +251,21 @@ void FriendSelectionWidget::loadIdentities() if(!rsIdentity->getIdentitiesSummaries(ids_meta)) { - std::cerr << __PRETTY_FUNCTION__ << " failed to retrieve identities group info for all identities" << std::endl; + RS_ERR("failed to retrieve identities group info for all identities"); return; - } - std::vector ids; + } - for(auto& meta:ids_meta) - ids.push_back(meta.mGroupId) ; + auto ids = std::make_unique>(); + for(auto& meta: ids_meta) ids->push_back(meta.mGroupId); - RsQThreadUtils::postToObject( [ids,this]() + RsQThreadUtils::postToObject( + [ids = std::move(ids), this]() { - /* Here it goes any code you want to be executed on the Qt Gui - * thread, for example to update the data model with new information - * after a blocking call to RetroShare API complete */ - - gxsIds = ids; // we do that is the GUI thread. Dont try it on another thread! - - fillList() ; - + // We do that is the GUI thread. Dont try it on another thread! + gxsIds = *ids; + /* TODO: To furter optimize away a copy gxsIds could be a unique_ptr + * too */ + fillList(); }, this ); }); } diff --git a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp index 4f1605c46..c258cb4bc 100644 --- a/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp +++ b/retroshare-gui/src/gui/statistics/GxsTransportStatistics.cpp @@ -20,6 +20,7 @@ #include #include +#include #include #include @@ -463,24 +464,25 @@ void GxsTransportStatistics::loadGroups() #ifdef DEBUG_FORUMS std::cerr << "Retrieving post data for post " << mThreadId << std::endl; #endif - std::map stats; + auto stats = std::make_unique< + std::map >(); - if(!rsGxsTrans->getGroupStatistics(stats)) - { - RsErr() << "Cannot retrieve group statistics in GxsTransportStatistics" << std::endl; - return; - } + if(!rsGxsTrans->getGroupStatistics(*stats)) + { + RS_ERR("Cannot retrieve group statistics in GxsTransportStatistics"); + return; + } - RsQThreadUtils::postToObject( [stats,this]() + RsQThreadUtils::postToObject( + [stats = std::move(stats), this]() { /* Here it goes any code you want to be executed on the Qt Gui * thread, for example to update the data model with new information * after a blocking call to RetroShare API complete */ - mGroupStats = stats; - - updateContent(); - + // TODO: consider making mGroupStats an unique_ptr to avoid copying + mGroupStats = *stats; + updateContent(); mStateHelper->setLoading(GXSTRANS_GROUP_META, false); }, this ); diff --git a/retroshare-gui/src/util/qtthreadsutils.h b/retroshare-gui/src/util/qtthreadsutils.h index 689161fde..1c377b35d 100644 --- a/retroshare-gui/src/util/qtthreadsutils.h +++ b/retroshare-gui/src/util/qtthreadsutils.h @@ -1,7 +1,7 @@ /******************************************************************************* * util/qthreadutils.h * * * - * Copyright (C) 2018 Gioacchino Mazzurco * + * Copyright (C) 2018-2020 Gioacchino Mazzurco * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU Affero General Public License as * @@ -27,7 +27,9 @@ #include #include + #include +#include namespace RsQThreadUtils { From 7c89140bcccc81f911e911c84c392cc64420f658 Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 25 Jun 2020 14:28:40 +0200 Subject: [PATCH 87/89] Fixing layout issues on Channel Composer --- .../gui/gxschannels/CreateGxsChannelMsg.ui | 265 +++++++++--------- 1 file changed, 140 insertions(+), 125 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui index d8baaba24..8912307e3 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui @@ -6,8 +6,8 @@ 0 0 - 671 - 513 + 736 + 271 @@ -20,7 +20,7 @@ :/images/logo/logo_16.png:/images/logo/logo_16.png - + 0 @@ -33,9 +33,6 @@ 0 - - 0 - @@ -52,40 +49,15 @@ QFrame::Raised - - - - - - Generate mass data - - - - - - - 1 - - - 999 - - - - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - + + 3 + + + 6 + + + 0 + @@ -102,46 +74,73 @@ - - - - - - - 0 - 0 - - - - - 75 - true - - - - Channel Post to: - - - - - - - true - - - true - - - - - + + 6 + + + 0 + + + 0 + + + 0 + + + 0 + + + 6 + + + QLayout::SetFixedSize + + + 6 + + + + + 6 + + + + + + 0 + 0 + + + + + 75 + true + + + + Channel Post to: + + + + + + + true + + + true + + + + + @@ -209,6 +208,20 @@ p, li { white-space: pre-wrap; } + + + + 6 + + + + + Title + + + + + @@ -217,7 +230,7 @@ p, li { white-space: pre-wrap; } 20 - 40 + 1 @@ -227,26 +240,7 @@ p, li { white-space: pre-wrap; } - - - Message - - - - 0 - - - - - Title - - - - - - - - + @@ -256,7 +250,7 @@ p, li { white-space: pre-wrap; } 0 - 0 + 6 0 @@ -309,7 +303,7 @@ p, li { white-space: pre-wrap; } 0 0 - 635 + 81 24 @@ -383,6 +377,23 @@ p, li { white-space: pre-wrap; } + + + + Channel Post + + + + :/icons/png/comment.png:/icons/png/comment.png + + + + 24 + 24 + + + + @@ -413,35 +424,39 @@ p, li { white-space: pre-wrap; } - - - - Channel Post - - - - :/icons/png/comment.png:/icons/png/comment.png - - - - 24 - 24 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - + + + + + + Generate mass data + + + + + + + 1 + + + 999 + + + + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel|QDialogButtonBox::Ok + + + + From 598f9f196490fcf53ed587c73dd0ee9b79c74bc0 Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 25 Jun 2020 14:36:38 +0200 Subject: [PATCH 88/89] Fixing margins --- retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui index 8912307e3..c0d28ac5f 100644 --- a/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui +++ b/retroshare-gui/src/gui/gxschannels/CreateGxsChannelMsg.ui @@ -33,6 +33,12 @@ 0 + + 6 + + + 0 + @@ -211,7 +217,7 @@ p, li { white-space: pre-wrap; } - 6 + 9 From ec92afa2fe9e6013d723ab42e3d99d6e9b5b418d Mon Sep 17 00:00:00 2001 From: defnax Date: Thu, 25 Jun 2020 16:36:16 +0200 Subject: [PATCH 89/89] Fixed to get work selection color on darkstylesheets * Fixed to get work selection color on dark stylesheets * Show group icon on the tabs --- .../gui/gxschannels/GxsChannelPostThumbnail.h | 2 ++ .../GxsChannelPostsWidgetWithModel.cpp | 16 ++++++++-------- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h index fd5c82d6b..4a0ae5855 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostThumbnail.h @@ -36,6 +36,8 @@ class ChannelPostThumbnailView: public QWidget { + Q_OBJECT + public: // This variable determines the zoom factor on the text below thumbnails. 2.0 is mostly correct for all screen. static constexpr float THUMBNAIL_OVERSAMPLE_FACTOR = 2.0; diff --git a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp index 6db53d2b6..73508c1e8 100644 --- a/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp +++ b/retroshare-gui/src/gui/gxschannels/GxsChannelPostsWidgetWithModel.cpp @@ -705,15 +705,15 @@ void GxsChannelPostsWidgetWithModel::groupNameChanged(const QString &name) QIcon GxsChannelPostsWidgetWithModel::groupIcon() { -// if (mStateHelper->isLoading(mTokenTypeGroupData) || mStateHelper->isLoading(mTokenTypeAllPosts)) { -// return QIcon(":/images/kalarm.png"); -// } + /* CHANNEL IMAGE */ + QPixmap chanImage; + if (mGroup.mImage.mData != NULL) { + GxsIdDetails::loadPixmapFromData(mGroup.mImage.mData, mGroup.mImage.mSize, chanImage,GxsIdDetails::ORIGINAL); + } else { + chanImage = FilesDefs::getPixmapFromQtResourcePath(ChannelPostThumbnailView::CHAN_DEFAULT_IMAGE); + } -// if (mNewCount) { -// return QIcon(":/images/message-state-new.png"); -// } - - return QIcon(); + return QIcon(chanImage); } /*************************************************************************************/